Diễn Đàn Teen Việt -Thế Giới 9x pro
Bạn có muốn phản ứng với tin nhắn này? Vui lòng đăng ký diễn đàn trong một vài cú nhấp chuột hoặc đăng nhập để tiếp tục.

giáo trình ngôn ngữ lập trinh C ++ <phần 8>

Go down

giáo trình ngôn ngữ lập trinh C ++ <phần 8> Empty giáo trình ngôn ngữ lập trinh C ++ <phần 8>

Bài gửi by luongbv93 25th November 2011, 10:17

CHƯƠNG 6 <Tiếp>

6.6 ĐA HÌNH

6.6.1 Đặt vấn đề

Sự kế thừa trong C++ cho phép có sự tương ứng giữa lớp cơ sở và các lớp dẫn xuất trong sơđồ
thừa kế:

• Một con trỏ có kiểu lớp cơ sở luôn có thể trỏđến địa chỉ của một đối tượng của lớp dẫn
xuất.

• Tuy nhiên, khi thực hiện lời gọi một phương thức của lớp, trình biên dịch sẽ quan tâm đến
kiểu của con trỏ chứ không phải đối tượng mà con trỏđang trỏ tới: phương thức của lớp
mà con trỏ có kiểu được gọi chứ không phải phương thức của đối tượng mà con trỏđang
trỏ tới được gọi.

Ví dụ, lớp Bus kế thừa từ lớp Car, cả hai lớp này đều định nghĩa phương thức show():

class Car{

public:

void show();

};

class Bus: public Car{

public:

void show();

};

khi đó, nếu ta khai báo một con trỏ lớp Bus, nhưng lại trỏ vào địa chỉ của một đối tượng lớp Car:

Bus myBus;

Car *ptrCar = &myBus; // đúng

nhưng khi gọi:

ptrCar->show();

thì chương trình sẽ gọi đến phương thức show() của lớp Car (là kiểu của con trỏ ptrCar), mà
không gọi tới phương thức show() của lớp Bus (là kiểu của đối tượng myBus mà con trỏ ptrCar
đang trỏ tới).

Để giải quyết vấn đề này, C++ đưa ra một khái niệm là phương thức trừu tượng. Bằng cách sử
dụng phương thức trừu tượng. Khi gọi một phương thức từ một con trỏđối tượng, trình biên dịch
sẽ xác định kiểu của đối tượng mà con trỏđang trỏđến, sau đó nó sẽ gọi phương thức tương ứng
với đối tượng mà con trỏđang trỏ tới.

6.6.2 Khai báo phương thức trừu tượng

Phương thức trừu tượng (còn gọi là phương thức ảo, hàm ảo) được khai báo với từ khoá virtual:

• Nếu khai báo trong phạm vi lớp:

virtual <Kiểu trả về> <Tên phương thức>([<Các tham số>]);

• Nếu định nghĩa ngoài phạm vi lớp:

virtual <Kiểu trả về> <Tên lớp>::<Tên phương thức>([<Các tham
số>]){…}

Ví dụ:

class Car{

public:

virtual void show();

};

là khai báo phương thức trừu tượng show() của lớp Car: phương thức không có tham số và không
cần giá trị trả về (void).

Lưu ý:

• Từ khoá virtual có thểđặt trước hay sau kiểu trả về của phương thức.

• Với cùng một phương thức được khai báo ở lớp cơ sở lẫn lớp dẫn xuất, chỉ cần dùng từ
khoá virtual ở một trong hai lần định nghĩa phương thức đó là đủ: hoặc ở lớp cơ sở, hoặc ở
lớp dẫn xuất.

• Trong trường hợp cây kế thừa có nhiều mức, cũng chỉ cần khai báo phương thức là trừu
tượng (virtual) ở một mức bất kì. Khi đó, tất cả các phương thức trùng tên với phương
thức đó ở tất cả các mức đều được coi là trừu tượng.

• Đôi khi không cần thiết phải định nghĩa chồng (trong lớp dẫn xuất) một phương thức đã
được khai báo trừu tượng trong lớp cơ sở.

6.6.3 Sử dụng phương thức trừu tượng – đa hình

Một khi phương thức được khai báo là trừu tượng thì khi một con trỏ gọi đến phương thức đó,
chương trình sẽ thực hiện phương thức tương ứng với đối tượng mà con trỏđang trỏ tới, thay vì

thực hiện phương thức của lớp cùng kiểu với con trỏ. Đây được gọi là hiện tượng đa hình (tương
ứng bội) trong C++.

Chương trình 6.9 minh hoạ việc sử dụng phương thức trừu tượng: lớp Bus kế thừa từ lớp Car, hai
lớp này cùng định nghĩa phương thức trừu tượng show().

• Khi ta dùng một con trỏ có kiểu lớp Car trỏ vào địa chỉ của một đối tượng kiểu Car, nó sẽ
gọi phương thức show() của lớp Car.

• Khi ta dùng cũng con trỏđó, trỏ vào địa chỉ của một đối tượng kiểu Bus, nó sẽ gọi phương
thức show() của lớp Bus.

Chương trình 6.9

#include<stdio.h>

#include<conio.h>

#include<string.h>

/* Định nghĩa lớp Car */

class Car{

private:

int speed; // Tốc độ

char mark[20]; // Nhãn hiệu

float price; // Giá xe

public:

int getSpeed(){return speed;};// Đọc tốc độ xe

char[ ] getMark(){return mark;};// Đọc nhãn xe

float getPrice(){return price;};// Đọc giá xe

// Khởi tạo thông tin về xe

Car(int speedIn=0, char markIn[ ]=””, float priceIn=0);

virtual void show(); // Giới thiệu xe, trừu tượng

};

/* Khai báo phương thức bên ngoài lớp */

Car::Car(int speedIn, char markIn[ ], float priceIn){

speed = speedIn;

strcpy(mark, markIn);

price = priceIn;

}

// Phương thức trừu tượng giới thiệu xe

virtual void Car::show(){

cout << “This is a ” << mark << “ having a speed of ”

<< speed << “km/h and its price is $” << price << endl;

return;

}
/* Định nghĩa lớp Bus kế thừa từ lớp Car */

class Bus: public Car{

int label; // Số hiệu tuyến xe

public:

// Khởi tạo đủ tham số

Bus(int sIn=0, char mIn[]=””, float pIn=0, int lIn=0);

void show(); // Giới thiệu xe

};

// Khởi tạo đủ tham số

Bus::Bus(int sIn, char mIn[ ], float pIn, int lIn):Car(sIn, mIn, pIn){

label = lIn;

}

// Định nghĩa nạp chồng phương thức trừu tượng

void Bus::show(){ // Giới thiệu xe bus

cout << “This is a bus of type ” << getMark() << “, on the line “

<< label << “, having a speed of ” << getSpeed()

<< “km/h and its price is $” << getPrice() << endl;

return;

}

// Chương trình chính

void main(){

clrscr();

Car *ptrCar, myCar(100, “Ford”, 3000);

Bus myBus(150, “Mercedes”, 5000, 27);// Biến đối tượng của lớp Bus

ptrCar = &myCar; // Trỏ đến đối tượng lớp Car

ptrCar->show(); // Phương thức của lớp Car

ptrCar = &myBus; // Trỏ đến đối tượng lớp Bus

ptrCar->show(); // Phương thức của lớp Bus

return;

}

Chương trình 6.9 hiển thị kết quả thông báo như sau:

This is a Ford having a speed of 100km/h and its price is $3000

This is a bus of type Mercedes, on the line 27, having a speed of
150km/h and its price is $5000

Dòng thứ nhất là kết quả khi con trỏ ptrCar trỏđến địa chỉ của đối tượng myCar, thuộc lớp Car
nên sẽ gọi phương thức show() của lớp Car với các dữ liệu của đối tượng myCar: (100, Ford,

3000). Dòng thứ hai tương ứng là kết quả khi con trỏ ptrCar trỏđến địa chỉ của đối tượng myBus,
thuộc lớp Bus nên sẽ gọi phương thức show() của lớp Bus, cùng với các tham số của đối tượng
myBus: (150, Mercedes, 5000, 27).

Lưu ý:

• Trong trường hợp ở lớp dẫn xuất không định nghĩa lại phương thức trừu tượng, thì chương
trình sẽ gọi phương thức của lớp cơ sở, nhưng với dữ liệu của lớp dẫn xuất.

Ví dụ, nếu trong chương trình 6.9, lớp Bus không định nghĩa chồng phương thức trừu tượng
show() thì kết quả hiển thị sẽ là hai dòng thông báo giống nhau, chỉ khác nhau ở dữ liệu của hai
đối tượng khác nhau:

This is a Ford having a speed of 100km/h and its price is $3000

This is a Mercedes having a speed of 150km/h and its price is $5000

TỔNG KẾT CHƯƠNG 6

Nội dung chương 6 đã trình bày các vấn đề cơ bản liên quan đến thừa kế và tương ứng bội trong
C++ như sau:

• Khai báo một lớp dẫn xuất kế thừa từ một lớp cơ sở bằng khai báo kế thừa “:” đi kèm với
một từ khoá dẫn xuất.

• Có ba loại dẫn xuất khác nhau, được quy định bởi ba từ khoá dẫn xuất khác nhau: private,
protected và public. Kiểu dẫn xuất phổ biến là dẫn xuất public.

• Sự kế thừa tạo ra mối quan hệ tương ứng giữa lớp cơ sở và lớp dẫn xuất: có thể chuyển
kiểu ngầm định (trong phép gán, phép truyền đối số, phép trỏđịa chỉ) từ một đối tượng
lớp cơ sởđến một đối tượng lớp dẫn xuất. Nhưng không thể chuyển kiểu ngược lại từ lớp
dẫn xuất vào lớp cơ sở.

• Hàm khởi tạo của lớp dẫn xuất có thể gọi tường minh hoặc gọi ngầm định hàm khởi tạo
của lớp cơ sở. Hàm khởi tạo lớp cơ sở bao giờ cũng được thực hiện trước hàm khởi tạo
lớp dẫn xuất.

• Hàm huỷ bỏ của lớp dẫn xuất luôn gọi ngầm định hàm huỷ bỏ của lớp cơ sở. Trái với hàm
khởi tạo, hàm huỷ bỏ lớp cơ sở luôn được thực hiện sau hàm huỷ bỏ của lớp dẫn xuất.

• Có thể truy nhập các phương thức của lớp cơ sở từ lớp dẫn xuất, phạm vi truy nhập là phụ
thuộc vào kiểu dẫn xuất: private, protected hoặc public. Điều này cho phép sử dụng lại mã
nguồn của lớp cơ sở, mà không cần định nghĩa lại ở lớp dẫn xuất.

• Trong lớp dẫn xuất, có thểđịnh nghĩa chồng một số phương thức của lớp cơ sở. Khi có
định nghĩa chồng, muốn truy nhập vào phương thức lớp cơ sở, phải dùng toán tử phạm vi
lớp “<Tên lớp>::”.

• C++ còn cho phép một lớp có thểđược dẫn xuất từ nhiều lớp cơ sở khác nhau, gọi là đa kế
thừa. Trong đa kế thừa, quan hệ giữa lớp dẫn xuất với mỗi lớp cơ sở là tương tự như trong
đơn kế thừa.

• Trong đa kế thừa, hàm khởi tạo lớp dẫn xuất sẽ gọi tường minh (hoặc ngầm định) hàm
khởi tạo các lớp cơ sở, theo thứ tự khai báo kế thừa. Hàm huỷ bỏ lớp dẫn xuất lại gọi
ngầm định các hàm huỷ bỏ của các lớp cơ sở.

• C++ cung cấp khái niệm kế thừa từ lớp cơ sở trừu tượng để tránh trường hợp trùng lặp dữ
liệu ở lớp dẫn xuất, khi các lớp cơ sở lại cùng được dẫn xuất từ một lớp khác.

• C++ cũng cho phép cơ chế tương ứng bội (đa hình) bằng cách định nghĩa một phương
thức là trừu tượng trong sơđồ thừa kế. Khi đó, một con trỏ lớp cơ sở có thể trỏđến địa chỉ
của một đối tượng lớp dẫn xuất, và phương thức được thực hiện là tuỳ thuộc vào kiểu của
đối tượng mà con trỏđang trỏ tới.

CÂU HỎI VÀ BÀI TẬP CHƯƠNG 6

1. Trong các khai báo sau, khai báo nào là đúng cú pháp kế thừa lớp:

a. class A: public class B{…};

b. class A: public B{…};

c. class A: class B{…};

d. class A:: public B{…};

2. Trong các kiểu dẫn xuất sau, từ các phương thức lớp dẫn xuất, không thể truy nhập đến
các thành phần private của lớp cơ sở:

a. private

b. protected

c. public

d. Cả ba kiểu trên

3. Trong các kiểu dẫn xuất sau, từđối tượng của lớp dẫn xuất, có thể truy nhập đến các thành
phần của lớp cơ sở:

a. private

b. protected

c. public

d. Cả ba kiểu trên

4. A là lớp dẫn xuất public từ lớp cơ sở B. Giả sử có các kiểu khai báo:

A myA, *ptrA;

B myB, *ptrB;

Khi đó, các lệnh nào sau đây là không có lỗi:

a. myA = myB;

b. myB = myA;

c. ptrA = &myB;

d. ptrB = &myA;

e. ptrA = ptrB;

f. ptrB = ptrA;

5. A là lớp dẫn xuất public từ lớp cơ sở B. Giả sử có các kiểu khai báo và nguyên mẫu hàm:

A myA;

B myB;

void show(A, B);
Khi đó, các lệnh gọi hàm nào sau đây là không có lỗi:

a. show(myA, myA);

b. show(myA, myB);

c. show(myB, myA);

d. show(myB, myB);

6. A là lớp dẫn xuất public từ lớp cơ sở B. Giả sử B có một hàm khởi tạo:

B(int, float);

Khi đó, định nghĩa hàm khởi tạo nào sau đây của lớp A là chấp nhận được:

a. A::A(){…};

b. A::A(): B(){…};

c. A::A(int x, float y): B(){…};

d. A::A(int x, float y): B(x, y){…};

7. A là lớp dẫn xuất public từ lớp cơ sở B. Giả sử B có hai hàm khởi tạo:

B();

B(int, float);

Khi đó, những định nghĩa hàm khởi tạo nào sau đây của lớp A là chấp nhận được:

a. A::A(){…};

b. A::A(): B(){…};

c. A::A(int x, float y): B(){…};

d. A::A(int x, float y): B(x, y){…};

8. A là lớp dẫn xuất public từ lớp cơ sở B. Giả sử B có hàm huỷ bỏ tường minh:

~B();

Khi đó, những định nghĩa hàm huỷ bỏ nào sau đây của lớp A là chấp nhận được:

a. A::~A(){…};

b. A::~A(): ~B(){…};

c. A::~A(int x){…};

d. A::~A(int x): ~B(){…};

9. Giả sử B là một lớp được khai báo:

class B{

int x;

public: int getx();

};

và A là một lớp dẫn xuất từ lớp B theo kiểu private:

class A: private B{

};

khi đó, nếu myA là một đối tượng lớp A, lệnh nào sau đây là chấp chận được:

a. myA.x;

b. myA.getx();

c. Cả hai lệnh trên.


d. Không lệnh nào cả.

10. Giả sử B là một lớp được khai báo:

class B{

int x;

public: int getx();

};

và A là một lớp dẫn xuất từ lớp B theo kiểu protected:

class A: protected B{

};

khi đó, nếu myA là một đối tượng lớp A, lệnh nào sau đây là chấp chận được:

a. myA.x;

b. myA.getx();

c. Cả hai lệnh trên.

d. Không lệnh nào cả.

11. Giả sử B là một lớp được khai báo:

class B{

int x;

public: int getx();

};

và A là một lớp dẫn xuất từ lớp B theo kiểu public:

class A: public B{

};

khi đó, nếu myA là một đối tượng lớp A, lệnh nào sau đây là chấp chận được:

a. myA.x;

b. myA.getx();

c. Cả hai lệnh trên.

d. Không lệnh nào cả.

12. Giả sử B là một lớp được khai báo:

class B{

public: void show();

};

và A là một lớp dẫn xuất từ lớp B theo kiểu public, có định nghĩa chồng hàm show():

class A: public B{

public: void show();

};

khi đó, nếu myA là một đối tượng lớp A, muốn thực hiện phương thức show() của lớp B
thì lệnh nào sau đây là chấp chận được:

a. myA.show();

b. myA.B::show();

c. B::myA.show();

d. A::B::show();

13. Muốn khai báo một lớp A kế thừa từ hai lớp cơ sở B và C, những lệnh nào là đúng:

a. class A: B, C{…};

b. class A: public B, C{…};

c. class A: public B, protected C{…};

d. class A: public B, public C{…};

14. B là một lớp có hai hàm khởi tạo:

B();

B(int);

C cũng là một lớp có hai hàm khởi tạo:

C();

C(int, int);

Và A là một lớp kế thừa từ B và C:

class A: public B, public C{…};

Khi đó, hàm khởi tạo nào sau đây của lớp A là chấp nhận được:

a. A::A(){…};

b. A::A():B(),C(){…};

c. A::A(int x, int y): C(x, y){…};

d. A::A(int x, int y, int z): B(x), C(y, z){…};

e. A::A(int x, int y, int z): C(x, y), B(z){…};

15. Muốn khai báo lớp A kế thừa từ lớp cơ sở trừu tượng B, những khai báo nào sau đây là
đúng:

a. virtual class A: public B{…};

b. class virtual A: public B{…};

c. class A: virtual public B{…};

d. class A: public virtual B{…};

16. Lớp A là một lớp dẫn xuất, được kế thừa từ lớp cơ sở B. Hai lớp này đều định nghĩa hàm
show(). Muốn hàm này trở thành trừu tượng thì những định nghĩa nào sau đây là đúng:

a. void A::show(){…} và void B::show(){…}

b. virtual void A::show(){…} và void B::show(){…}

c. void A::show(){…} và virtual void B::show(){…}

d. virtual void A::show(){…} và virtual void B::show(){…}

17. Khai báo lớp người (Human) bao gồm các thuộc tính sau:

• Tên người (name)

• Tuổi của người đó (age)

• Giới tính của người đó (sex)

Sau đó khai báo lớp Cá nhân (Person) kế thừa từ lớp Human vừa được định nghĩa ở trên.

18. Bổ sung các phương thức truy nhập các thuộc tính của lớp Human, các phương thức này
có tính chất public.

19. Bổ sung thêm các thuộc tính của lớp Person: địa chỉ và sốđiện thoại. Thêm các phương
thức truy nhập các thuộc tính này trong lớp Person.

20. Xây dựng hai hàm khởi tạo cho lớp Human: một hàm không tham số, một hàm với đủ ba
tham số tương ứng với ba thuộc tính của nó. Sau đó, xây dựng hai hàm khởi tạo cho lớp
Person có sử dụng các hàm khởi tạo của lớp Human: một hàm không tham số, một hàm đủ
năm tham số (ứng với hai thuộc tính của lớp Person và ba thuộc tính của lớp Human).

21. Xây dựng một hàm main, trong đó có yêu cầu nhập các thuộc tính để tạo một đối tượng có
kiểu Human và một đối tượng có kiểu Person, thông qua các hàm set thuộc tính đã xây
dựng.

22. Xây dựng hàm show() cho hai lớp Human và Person. Thay đổi hàm main: dùng một đối
tượng có kiểu lớp Person, gọi hàm show() của lớp Person, sau đó lại gọi hàm show() của
lớp Human từ chính đối tượng đó.

23. Khai báo thêm một lớp người lao động (Worker), kế thừa từ lớp Human, có thêm thuộc
tính là số giờ làm việc trong một tháng (hour) và tiền lương của người đó (salary). Sau đó,
khai báo thêm một lớp nhân viên (Employee) kế thừa đồng thời từ hai lớp: Person và
Worker. Lớp Employee có bổ sung thêm một thuộc tính là chức vụ (position).

24. Chuyển lớp Human thành lớp cơ sở trừu tượng của hai lớp Person và Worker. Xây dựng
thêm hai hàm show() của lớp Worker và lớp Employee. Trong hàm main, khai báo một đối
tượng lớp Employee, sau đó gọi đến các hàm show() của các lớp Employee, Person,
Worrker và Human.

25. Chuyển hàm show() trong các lớp trên thành phương thức trừu tượng. Trong hàm main,
khai báo một con trỏ kiểu Human, sau đó, cho nó trỏđến lần lượt các đối tượng của các
lớp Human, Person, Worker và Employee, mỗi lần đều gọi phương thức show() để hiển thị
thông báo ra màn hình.
luongbv93
luongbv93
Admin

Tổng số bài gửi : 117
Points : 354
Reputation : 0
Join date : 18/11/2011
Age : 30
Đến từ : Hà Nội

https://diendanteenviet.forumvi.com

Về Đầu Trang Go down

Về Đầu Trang

- Similar topics

 
Permissions in this forum:
Bạn không có quyền trả lời bài viết