You are on page 1of 32

Chương 4.

Dẫn xuất và kế thừa


trong C++

Bộ môn Tin học


Khoa HTTTKT & TMĐT

8/4/2020 Chương 4: Dẫn xuất và kế thừa trong 1


C++
Nội dung
 Sự dẫn xuất và tính kế thừa
 Hàm ảo và tính đa hình
 Đa kế thừa trong C++

Chương 4: Dẫn xuất và kế thừa trong C++ 2


4.1. Sự dẫn xuất và tính thừa kế
1. Lớp cơ sở và lớp dẫn xuất
2. Cách xây dựng lớp cơ sở
3. Thừa kế các thành phần dữ liệu
4. Thừa kế các hàm thành phần

Chương 4: Dẫn xuất và kế thừa trong C++ 3


4.1.1 Lớp cơ sở và lớp dẫn xuất

 Giới thiệu: Kế thừa từ các lớp có từ trước.


 Ích lợi: có thể tận dụng lại
 Các thuộc tính chung
 Các hàm có thao tác tương tự

Lớp cơ sở LỚP CHA


(Base class) (Super class) STUDENT

Lớp dẫn xuất LỚP CON


(Derived class) (Sub class) CIT_STUDENT

Chương 4: Dẫn xuất và kế thừa trong C++ 4


Ví dụ minh họa
Ký hiệu n
composition ContactDir Contact

class Contact { class ContactDir {


private: private:
char *name; // ten doi tac int Lookup(const char *name);
char *address; // dia chi doi tac Contact **contacts; // ds cac doi tac
char *tel; // so dien thoai
int dirSize; // kich thuoc thu muc hien tai
public:
int maxSize; // kich thuoc thu muc toi da
Contact (const char *name,
const char *address, const char *tel); public:
~Contact (); ContactDir (const int maxSize);
const char* Name () const { return name;} ~ContactDir();
const char* Address() const { return address;} void Insert(const Contact&);
const char* Tel() const { return tel;} void Delete(const char *name);
}; Contact* Find(const char *name);
// …………
};

Chương 4: Dẫn xuất và kế thừa trong C++ 5


Ví dụ minh họa (tt)
n
ContactDir Contact
Ký hiệu
Thừa kế

SmartDir

class SmartDir : public ContactDir { Contact* SmartDir::Recent (void) {


private: return recent == 0 ? 0 :
char *recent; // ten duoc tim gan nhat ContactDir::Find(recent);
public: }
SmartDir(const int max) : ContactDir(max) Contact* SmartDir::Find (const char *name) {
{ recent = 0; } Contact *c = ContactDir::Find(name);
Contact* Recent (void); if (c != 0)
Contact* Find (const char *name); recent = (char*) c->Name();
// ……………. return c;
}; }

Chương 4: Dẫn xuất và kế thừa trong C++ 6


4.1.1 Lớp cơ sở và lớp dẫn xuất (tiếp)
 Tính thừa kế
 Cho phép nâng cao khả năng sử dụng lại các bộ phận của
chương trình
 Cho phép định nghĩa một lớp mới (lớp dẫn xuất) từ một lớp
đã có (lớp cơ sở). Lớp dẫn xuất sẽ thừa kế các thành phần của
lớp cơ sở, đồng thời thêm vào các thành phần mới.
 Thừa kế cho phép không cần phải biên dịch lại các thành
phần chương trình đã có trong lớp cơ sở và không phải có
chương trình nguồn tương ứng
 Thừa kế cho phép nhiều lớp có thể được dẫn xuất từ một lớp
cơ sở.
 Không giới hạn mức: một lớp dẫn xuất lại có thể là lớp cơ sở
cho lớp dẫn xuất khác.
Chương 4: Dẫn xuất và kế thừa trong C++ 7
4.1.1 Lớp cơ sở và lớp dẫn xuất (tiếp)
 Tính đa hình
 Được thiết lập trên cơ sở kế thừa, trong đó đối tượng có thể có
biểu hiện khác nhau tùy thuộc vào từng tình huống cụ thể
 Ví dụ: đối tượng contractdir vừa là đối tượng của lớp ContractDir
và lớp SmartDir, nhưng hàm find() có hai cài đặt tương ứng ở lớp
ContractDir và SmartDir.

Chương 4: Dẫn xuất và kế thừa trong C++ 8


4.1.2 Cách xây dựng lớp dẫn xuất
 Trong thừa kế, khi khởi tạo đối tượng:
 Hàm xây dựng của lớp cha sẽ được gọi trước
 Sau đó mới là hàm xây dựng của lớp con.
 Trong thừa kế, khi hủy bỏ đối tượng:
 Hàm hủy của lớp con sẽ được gọi trước
 Sau đó mới là hàm hủy của lớp cha.
A

Chương 4: Dẫn xuất và kế thừa trong C++ 9


4.1.2 Cách xây dựng lớp dẫn xuất
Mặt hàng
 Ví dụ về thừa kế nhiều mức
Thuộc tính:
Tên
Số lượng trong kho
Giá mua
Giá bán
Phương thức
tínhlãi
mua(n)
mặt hàng nhập khẩu bán(n)
Thuộc tính:
Thuế nhập
Phương thức
tínhlãi

xe máy nhập khẩu

Thuộc tính:
Dung tích xi lanh

Chương 4: Dẫn xuất và kế thừa trong C++ 10


4.1.2 Cách xây dựng lớp dẫn xuất (tiếp)
Ví dụ: trong đơn thừa kế
class point{ class colorpoint: public point{
int x,y; int color;
public: public:
point(){ x=0; y=0;} colorpoint():point(){ color=0;}
point(int x1, int y1){ x=x1; y=y1;} colorpoint(int x1, int y1,int
void move (int dx, int c):point(x1,y1){ color=c;}
dy){x+=dx;y+=dy;} void display(){
void display(){ cout<< “Goi colorpoint::display() \n
cout<< “Goi ham point::display() \n ”;
”; point::display();
cout << “toa do”<<x<<y;} cout << “mau”<<color;}
}; };

Chương 4: Dẫn xuất và kế thừa trong C++ 11


4.1.2 Cách xây dựng lớp dẫn xuất (tiếp)
void main()
{
colorpoint m; // color= 0
m.display()
m.point::display(); //Chi hien thi toa do cua m
colorpoint n(1,1,1); // color = 1
n.display();
n.point::display(); //chi hien thi toa do cua n
}

Chương 4: Dẫn xuất và kế thừa trong C++ 12


4.1.3 Kế thừa các thành phần (dữ liệu – hàm)

 Truy nhập các thành phần của lớp cơ sở từ lớp dẫn xuất
 Các thành phần private trong lớp cơ sở ko thể truy nhập được từ
các lớp dẫn xuất. Ví dụ x,y của point ko thể được dùng colorpoint
 Lớp dẫn xuất được phép truy nhập đến các hàm thành phần
protected và public trong lớp cơ sở.

Lớp cơ sở LỚP CHA


(Base class) (Super class) STUDENT

Lớp dẫn xuất LỚP CON


(Derived class) (Sub class) CIT_STUDENT

Chương 4: Dẫn xuất và kế thừa trong C++ 13


4.1.3 Kế thừa các thành phần (dữ liệu – hàm)

 Các kiểu dẫn xuất:


 Dẫn xuất public: Các thành phần, các hàm bạn và các đối tượng của lớp dẫn
xuất ko thể truy nhập đến các thành phần private của lớp cơ sở. Các thành
phần protected trong lớp cơ sở trở thành private trong lớp dẫn xuất. Các thành
phần public trong lớp cơ sở vẫn là public trong lớp dẫn xuất.

Chương 4: Dẫn xuất và kế thừa trong C++ 14


4.1.3 Kế thừa các thành phần (dữ liệu – hàm)
 Dẫn xuất private: Các thành phần public của lớp cơ sở trở thành private trong
lớp dẫn xuất. Các thành phần protected trong lớp cơ sở có thể truy nhập được
từ các hàm thành phần và các hàm bạn của lớp dẫn xuất. (Dẫn xuất này chỉ sử
dụng trong một số trường hợp đặc biệt khi mà lớp dẫn xuất không khai báo
thêm các thành phần hàm mới mà chỉ định nghĩa lại các phương thức đã có
trong lớp cơ sở.)

 Dẫn xuất protected: các thành phần public, protected trong lớp cơ sở trở
thành các thành phần protected trong lớp dẫn xuất.

Chương 4: Dẫn xuất và kế thừa trong C++ 15


4.1.3 Kế thừa các thành phần (dữ liệu – hàm)

Lớp cơ sở Thừa kế public Thừa kế private Thừa kế protected


private _ _ _
public public private protected
protected protected private protected

class B : A { // Thừa kế dạng private


class A { …….
private: };
int x; class C : private A { // A là lớp cơ sở riêng của B
void Fx (void); ………
public: };
int y; class D : public A { // A là lớp cơ sở chung của C
void Fy (void); ………
protected: };
int z;
void Fz (void); class E : protected A { // A: lớp cơ sở được bảo
}; //vệ của lớp E
……….
};
Chương 4: Dẫn xuất và kế thừa trong C++ 16
4.1.3 Kế thừa các thành phần (dữ liệu – hàm)

 Đ/N lại các hàm thành phần của lớp cơ sở trong lớp dẫn xuất
 Hàm display() đã đ/n trong lớp point nhưng được đ/n lại trong
lớp colorpoint. Lúc này có hai phiên bản khác nhau của
display() tồn tại trong lớp colorpoint (point::display() và
colorpoint::display())
 Trong phạm vi lớp dẫn xuất hàm thứ hai che lấp hàm thứ nhất
 Đ/n lại hàm thành phần khác với đ/n chồng hàm thành phần:
 Hàm đ/n lại và hàm bị định nghĩa lại giống hệt nhau về tên, kiểu trả về và
tham số, chúng chỉ khác nhau về vị trí, một hàm đặt ở lớp dẫn xuất và
một hàm đặt ở lớp cơ sở

Chương 4: Dẫn xuất và kế thừa trong C++ 17


4.1.3 Kế thừa các thành phần (dữ liệu – hàm)
 Khai báo trong lớp dẫn xuất các thành phần dữ liệu cùng tên với các
thành phần dữ liệu đã có trong lớp cơ sở. Hai thành phần cùng tên
này có thể cùng hay khác kiểu.
 Lúc này bên trong đối tượng của lớp dẫn xuất có hai thành phần khác nhau có
cùng tên, nhưng trong phạm vi lớp dẫn xuất tên chung đó chỉ định thành phần
được khai báo lại trong lớp dẫn xuất.
 Muốn chỉ định thành phần cùng tên trong lớp cơ sở phải chỉ ra tên lớp cơ sở
cùng toán tử phạm vi “::”.

Chương 4: Dẫn xuất và kế thừa trong C++ 18


4.1.3 Kế thừa các thành phần (dữ liệu – hàm)

 Hàm thiết lập trong lớp


 Nếu lớp có hàm thiết lập tường minh thì khi tạo ra đối tượng
sẽ có lời gọi đến hàm thiết lập đó. Việc chọn hàm thiết lập dựa
vào các tham số được cung cấp kèm theo. Trường hợp không
có hàm thiết lập nào phù hợp sẽ sinh ra lỗi.
 Phân cấp lời gọi
 Một đối tượng lớp dẫn xuất về thực chất có thể coi là đối
tượng lớp cơ sở, do đó việc gọi hàm thiết lập lớp dẫn xuất để
tạo đổi tượng lớp dẫn xuất sẽ kéo theo việc gọi đến một hàm
thiết lập lớp cơ sở.
 Về nguyên tắc, những phần của đối tượng lớp dẫn xuất thuộc
về lớp cơ sở sẽ được tạo ra trước khi các thông tin mới được
xác lập

Chương 4: Dẫn xuất và kế thừa trong C++ 19


4.1.3 Kế thừa các thành phần (dữ liệu – hàm)
 Sự tương thích giữa đối tượng lớp dẫn xuất và cơ sở
 Một đối tượng lớp dẫn xuất có thể thay thế đối tượng lớp cơ
sở. Tất cả các thành phần dữ liệu có trong lớp cơ sở đều tìm
thấy trong lớp dẫn xuất, tất cả các phương thức thực hiện
được trên lớp cơ sở luôn luôn có thể làm được trên lớp dẫn
xuất.
 Có thể chuyển kiểu ngầm định từ một đối tượng thuộc lớp dẫn
xuất sang đối tượng lớp cơ sở.
 VD:

point p; colorpoint pc;


p=pc;// pc=p là sai;

Chương 4: Dẫn xuất và kế thừa trong C++ 20


4.2 Hàm ảo và tính đa hình
 Ví dụ Mặt hàng

Thuộc tính:
Tên
Số lượng trong kho
Giá mua
Giá bán
Phương thức
Tínhlãi()
mua(n)
bán(n)

mặt hàng nhập khẩu mặt hàng xuất khẩu


Thuộc tính: Thuộc tính:
Thuế nhập Thuế xuất
Phương thức Phương thức
Tínhlãi() Tínhlãi()

Chương 4: Dẫn xuất và kế thừa trong C++ 21


4.2.1 Hàm ảo và tính đa hình
 Một tham trỏ của lớp cơ sở có thể nhận địa chỉ của đối tượng của lớp
thừa kế. Vì thế, lời gọi đến một phương thức của một đối tượng được
trỏ tới luôn được coi như lời gọi đến phương thức tương ứng với
kiểu con trỏ chứ không phải tương ứng với đối tượng đang được trỏ
đến.
 Ví dụ:
Mat_hang *mh;
Mat_hang_xuat_khau p;
Mat_hang_nhap_khau q;
mh= &p; mh.tinh_lai(); // kết quả tính lãi như mặt hàng nói chung
mh= &q; mh.tinh_lai(); // kết quả tính lãi như mặt hàng nói chung

 Sử dụng hàm ảo
Chương 4: Dẫn xuất và kế thừa trong C++ 22
4.2.1 Hàm ảo và tính đa hình
 Ví dụ:
class coloredpoint : public point{
Class point {
int color;
float x,y; coloredpoint() : point() {color=0;}
public: void display(){
point(){ x=0;y=0; }; point::display();
point(float ox, float oy){ cout<<color;
}
x=ox; y=oy;
coloredpoint(float ox, float oy, int c):
}; point(ox,oy) {
virtual void display(){ color=c;
cout<<x<<y; }
} };
};

Chương 4: Dẫn xuất và kế thừa trong C++ 23


4.2.1 Hàm ảo và tính đa hình
void main(){
coloredpoint pc(1,1,1);
pc.display();
point *ptr=&pc;
ptr->display(); // sử dụng hàm display của lớp coloredpoint
point p(2,2);
ptr=&p;
ptr->display(); // sử dụng hàm display của lớp point
}

Chương 4: Dẫn xuất và kế thừa trong C++ 24


4.2.2 Hàm ảo thuần túy và lớp trừu tượng

 Hàm ảo thuần túy là hàm mà không có phần định nghĩa.


Cụ thể:
virtual void tên_hàm() =0;
 (theo C++)Lớp có ít nhất một hàm thành phần ảo thuần túy
được gọi là lớp trừu tượng (lớp cơ sở trừu tượng).
 Tổng quát: lớp nào chỉ làm nhiệm vụ là lớp cơ sở cho các
lớp khác gọi là lớp cơ sở trừu tượng
 Ý nghĩa: chỉ dùng làm cơ sở cho các lớp khác, chỉ được
dùng để định nghĩa một số khái niệm tổng quát, chung cho
các lớp khác

Chương 4 - Dẫn xuất và kế thừa 25


4.2.2 Hàm ảo thuần túy và lớp trừu tượng

 Đặc điểm:
 Không có đối tượng nào của môt lớp trừu tượng được
khai báo (tức không sử dụng lớp trừu tượng để khai báo
các biến)
 Một hàm ảo thuần túy khai báo trong lớp trừu tượng
phải được định nghĩa lại trong một lớp dẫn xuất hoặc
nếu không thì phải được tiếp tục khai báo ảo trong lớp
dẫn xuất

Chương 4 - Dẫn xuất và kế thừa 26


4.3. Đa kế thừa trong C++
 Đa kế thừa
 Lớp cơ sở ảo
 Hàm thiết lập và hủy bỏ với lớp ảo

Chương 4: Dẫn xuất và kế thừa trong C++ 27


4.3.1 Đa kế thừa trong C++

OptionList Window class OptionList { class Window {


public: public:
OptionList (int n); Window (Rect &);
~OptionList (); ~Window (void);
Menu //... //...
}; };

class Menu
OptionList object Window object Menu object : public OptionList, public Window {
OptionList public:
Window OptionList
data members
data members data members Menu (int n, Rect &bounds);
~Menu (void);
Window
//...
data members };
Menu Menu::Menu (int n, Rect &bounds) :
data members
OptionList(n), Window(bounds)
{ /* ... */ }
Chương 4: Dẫn xuất và kế thừa trong C++ 28
Sự mơ hồ trong đa thừa kế

class OptionList { class Window {


public: public:
// …… // ……
void Highlight (int part); void Highlight (int part);
}; };

class Menu : public OptionList,


Hàm cùng tên Chỉ rõ hàm
public Window của lớp nào
{ ……. };

void main() {
Gọi void main() {
hàm xử lý Menu m1(….);
Menu m1(….);
của lớp m1.OptionList::Highlight(10);
nào ? m1.Highlight(10);
m1.Window::Highlight(20);
….
….
}
}

Chương 4: Dẫn xuất và kế thừa trong C++ 29


4.3.2 Lớp cơ sở ảo
 Ý nghĩa: Giải quyết tình trạng thừa kế xung đột,
 Khai báo: dùng từ khóa virtual khi khai báo lớp
dẫn xuất
Class lớp_dẫn_xuất: public virtual lớp_cơ_sở {
…};
 Lưu ý: từ khóa virtual và từ khóa phạm vi có thể
đảo vị trí
 Ví dụ:

Chương 4 - Dẫn xuất và kế thừa 30


4.3.2 Lớp cơ sở ảo –ví dụ
class A{ class A{
… …
int x,y; int x,y;
}; };
class B:public A{…}; class B: virtual public A{…};
class C:public A {…}; class C:public virtual A {…};
class D: public B, public C {…}; class D: public B, public C {…};

 Các thành phần của lớp A sẽ  A được khai báo ảo trong B và


xuất hiện trong D hai lần C;
 Dư thừa dữ liệu?  A chỉ xuất hiện một lần trong
 Muốn sử dụng cả hai bản sao các con cháu của chúng.
của A
A::B::x và A::C::x

Chương 4 - Dẫn xuất và kế thừa 31


4.3.3 Hàm thiết lập và hủy bỏ với lớp cơ sở ảo

 Trong đa kế thừa:
 Các hàm thiết lập được gọi theo thứ tự xuất hiện trong
danh sách khai báo các lớp cơ sở.
 Sau đó đến hàm thiết lập của lớp dẫn xuất.
 Với lớp cơ sở ảo: hàm thiết lập của một lớp ảo luôn
được gọi trước các hàm thiết lập khác.

Chương 4 - Dẫn xuất và kế thừa 32

You might also like