Professional Documents
Culture Documents
LTHĐT - Slide BG - Chương 4 - Update
LTHĐT - Slide BG - Chương 4 - Update
CHƯƠNG 4.
KỸ THUẬT KẾ THỪA
2
CÁC NỘI DUNG CHÍNH
4.2. Đa kế thừa
3
GIỚI THIỆU
Thừa kế là một kỹ thuật quan trọng trong lập trình hướng đối tượng. Kỹ
thuật này cho phép lập trình viên sử dụng lại mã lệnh chương trình. Từ đó giúp
làm giảm thời gian và công sức cần thiết để phát triển chương trình đồng thời
tăng sức tin cậy của chương trình đó
Kế thừa cho phép các lớp được xây dựng từ các lớp khác đã có
Một lớp được xây dựng kế thừa một lớp khác gọi là lớp dẫn xuất (derived
class).
Lớp dùng để xây dựng lớp dẫn xuất gọi là lớp cơ sở (base class).
4
GIỚI THIỆU
Lớp dẫn xuất sẽ kế thừa:
Lớp nào trong chương trình cũng có thể là một lớp cơ sở hay cụ thể hơn
một lớp dẫn xuất có thể là lớp cơ sở cho một lớp khác
5
GIỚI THIỆU
Một số sơ đồ minh họa tính kế thừa
Động vật
point
6
GIỚI THIỆU
Một số sơ đồ minh họa tính kế thừa
Công dân
Trưỏng bộ môn
7
GIỚI THIỆU
Tính kế thừa và đặc trưng của lớp kế thừa
Lớp cơ sở
Đặc trưng A
Các đặc trưng này
Đặc trưng B được định nghĩa ở
Đặc trưng C lớp cơ sở
class <Tên_lớp_cơ_sở>
{
// Nội dung lớp cơ sở
};
class <Tên_lớp_dẫn_xuất> : Kiểu_thừa_kế <Tên_lớp_cơ_sở>
{
//Nội dung lớp dẫn xuất
};
9
4.1. ĐƠN KẾ THỪA
Trong đó:
Kiểu_thừa_kế có thể là:
public
private
protected
Kiểu thừa kế chỉ rõ các thành phần của lớp cơ sở sẽ được kế
thừa theo cách nào.
Nếu được bỏ trống chương trình ngầm hiểu là private.
10
4.1. ĐƠN KẾ THỪA
Ví dụ
11
4.1. ĐƠN KẾ THỪA
class Box
{
float hight, weight;
public:
Lớp cơ sở
void Set()
{
cout<<"\n Input hight: "; cin>> hight;
cout<<"\n Input weight: "; cin>> weight;
}
void Display()
{
cout<<"hight = "<<hight<<"\tweight= "<<weight<<endl;
}
};
12
4.1. ĐƠN KẾ THỪA
class ColorBox: public Box
{ int color;
Lớp dẫn
public: xuất
void SetColor()
{ Set();
cout<<"\n Input color: "; cin>>color;
}
void DisplayColor()
{
Display();
cout<<"color= "<<color<<endl;
}
}; 13
4.1. ĐƠN KẾ THỪA
main()
{ Box a;
ColorBox b;
b.SetColor();
b.DisplayColor();
14
}
4.1. ĐƠN KẾ THỪA
15
4.1. ĐƠN KẾ THỪA
Chú ý:
Trong C++ không phải mọi hàm thành phần đều có thể truyền qua tính kế
thừa. Cụ thể một số trường hợp sau đây không cho phép sử dụng tính kế
thừa.
• Hàm tạo.
• Hàm hủy.
• Các toán tử mới do người dùng định nghĩa.
• Các toán tử gán do người dùng định nghĩa
• Quan hệ friend.
16
4.1. ĐƠN KẾ THỪA
Chú ý:
Lớp dẫn xuất không được phép truy nhập đến thành phần private của lớp cơ sở.
Được phép khai báo bên trong lớp dẫn xuất các thành phần cùng tên với các
thành phần đã có trong lớp cơ sở.
<Hai thành phần cùng tên này có thể cùng kiểu hay khác kiểu. Lúc này bên trong một đối tượng của
lớp dẫn xuất có tới 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
đó nhằm chỉ định thành phần được khai báo lại trong lớp dẫn xuất.>
Khi muốn chỉ định thành phần cùng tên trong lớp cơ sở, phải sử dụng
tên lớp cơ sở và toán tử phạm vi :: đặt trước tên thành phần đó với cú pháp:
<Tên_lớp_cơ_sở> :: <Tên_thành_phần>
Trong trường hợp chương trình sử dụng nhiều mức độ kế thừa khác nhau, toán
tử phạm vi vẫn cho phép truy nhập đến bất kể thành phần của lớp cơ sở nào.
17
4.1. ĐƠN KẾ THỪA
Ghi đè hàm thành phần của lớp cơ sở
Một lớp dẫn xuất có thể ghi đè hàm (overriding function) thành phần lớp cơ
sở. Hàm ghi đè và hàm bị ghi đè giống hệt nhau về tên, tham số và giá trị
trả về, chúng chỉ khác nhau ở vị trí, một hàm đặt trong lớp dẫn xuất và hàm
kia thì có mặt trong lớp cơ sở.
Một đối tượng lớp dẫn xuất gọi đến hàm ghi đè hoặc khi hàm đó được gọi
trong lớp dẫn xuất, hàm ghi đè của lớp dẫn xuất được gọi một cách tự
động. Muốn gọi đến hàm đó ở lớp cơ sở cần sử dụng toán tử định phạm
vi :: đặt trước tên hàm.
Ví dụ: Tạo lớp điểm trên mặt phẳng point có thành phần dữ liệu x,y. Lớp
điểm màu coloredpoint kế thừa từ lớp point và bổ sung thành phần màu
color:
18
4.1. ĐƠN KẾ THỪA
class point
{ float x,y;
public:
void Set(int a, int b)
{ x=a; y=b; }
void display()
{ cout<<"Toa do : "<<x<<" "<<y<<endl;}
};
class coloredpoint : public point
{ unsigned int color;
public:
void SetColor(int mau)
{ color= mau;}
void display() /* Ghi đè hàm display() của lớp cơ sở*/
{point::display(); /*gọi tới hàm cùng tên trong lớp cơ sở*/
cout<<"Mau "<<color<<endl;}
19
4.1. ĐƠN KẾ THỪA
main()
coloredpoint n ;
n.Set(4,5);
n.SetColor(6);
} 20
4.1. ĐƠN KẾ THỪA
Hàm tạo và hàm hủy trong kế thừa
Hàm tạo: Cú pháp định nghĩa hàm tạo lớp dẫn xuất:
Hàmtạolớpdẫnxuất(Dsthamsố) : Hàmtạolớpcơsở(Dsthamsố)
21
4.1. ĐƠN KẾ THỪA
Hàm tạo và hàm hủy trong kế thừa
Hàm tạo: Quy tắc định nghĩa hàm tạo lớp dẫn xuất như sau:
- Nếu lớp cơ sở và lớp dẫn xuất không định nghĩa hàm tạo thì hàm tạo
ngầm định của hai lớp sẽ đươc gọi theo đúng thứ tự.
- Nếu lớp cơ sở và lớp dẫn xuất có định nghĩa hàm tạo thì khi định nghĩa
hàm tạo lớp dẫn xuất luôn mô tả một lời gọi tới một trong các hàm tạo
tương ứng ở lớp cơ sở.
+ Hàm tạo không tham số của lớp dẫn xuất có thể có hoặc không cần
mô tả lại một lời gọi đến hàm tạo không tham số lớp cơ sở.
+ Hàm tạo có tham số của lớp dẫn xuất cần thiết phải mô tả lại một
lời gọi đến hàm tạo có tham số lớp cơ sở tương ứng. 22
4.1. ĐƠN KẾ THỪA
Hàm tạo và hàm hủy trong kế thừa
Hàm hủy: Hàm hủy thứ tự gọi ngược lại với hàm tạo. Khi hủy đối tượng
hàm hủy lớp dẫn xuất được gọi trước, rồi đến hàm hủy lớp cơ sở.
23
4.1. ĐƠN KẾ THỪA
Hàm tạo và hàm hủy trong kế thừa
Nhận xét:
Nếu muốn sử dụng hàm tạo không tham số của lớp cơ sở thì có thể
không cần mô tả cùng với định nghĩa của hàm tạo lớp dẫn xuất
Các tham số mà hàm tạo lớp dẫn xuất truyền cho hàm tạo lớp cơ sở
không nhất thiết lấy nguyên si từ các tham số nó nhận được mà có thể
được thay đổi đi ít nhiều.
Nếu lớp có thành phần dữ liệu con trỏ phải định nghĩa hàm hủy ngược lại
không bắt buộc.
24
4.1. ĐƠN KẾ THỪA
Hàm tạo và hàm hủy trong kế thừa
Nếu trong lớp dẫn xuất không khai báo tường minh hàm tạo sao chép, thì
công việc này được chương trình biên dịch đảm nhiệm nhờ định nghĩa hàm
tạo sao chép ngầm định.
Về nguyên tắc, trong định nghĩa của hàm tạo sao chép lớp dẫn xuất có
thể mô tả bất kỳ hàm tạo sao chép hoặc hàm tạo nào có mặt trong lớp cơ
sở. Lớp có thành phần dữ liệu con trỏ phải định nghĩa hàm tạo sao chép,
ngược lại không bắt buộc.
25
4.1. ĐƠN KẾ THỪA
Con trỏ và kế thừa
• Tương thích giữa đối tượng thuộc lớp dẫn xuất với đối tượng
thuộc lớp cơ sở:
Một đối tượng của lớp dẫn xuất có thể thay thế một đối tượng của lớp cơ
sở. Nghĩa là tất cả các thành phần của lớp cơ sở đều tìm thấy trong lớp
dẫn xuất.
Một đối tượng lớp cơ sở không thể thay thế một đối tượng lớp dẫn xuất.
26
4.1. ĐƠN KẾ THỪA
Con trỏ và kế thừa
• Tương thích giữa con trỏ lớp dẫn xuất và con trỏ lớp cơ sở :
Một con trỏ đối tượng lớp cơ sở có thể trỏ đến một đối tượng lớp dẫn
xuất.
Một con trỏ lớp dẫn xuất không thể trỏ đến đối tượng lớp cơ sở trừ
trường hợp ép kiểu.
27
4.1. ĐƠN KẾ THỪA
Con trỏ và kế thừa
• Tương thích giữa tham chiếu lớp dẫn xuất và tham chiếu lớp cơ
sở :
Một tham chiếu đối tượng lớp cơ sở có thể tham chiếu đến một đối
tượng lớp dẫn xuất.
Một tham chiếu lớp dẫn xuất không thể tham chiếu đến đối tượng lớp cơ
sở trừ trường hợp ép kiểu.
28
4.1. ĐƠN KẾ THỪA
Phạm vi truy xuất
• Khi thiết lập quan hệ kế thừa, vẫn cần phải quan tâm đến tính đóng gói
và che dấu thông tin. Vì vậy cần xác định ảnh hưởng của kế thừa đến phạm
vi truy xuất các thành phần của lớp. Hai vấn đề được đặt ra là:
- Hàm thành phần của lớp dẫn xuất có quyền truy xuất các thành phần
riêng tư của lớp cơ sở hay không.
- Các thành phần của lớp cơ sở, sau khi kế thừa xuống lớp dẫn xuất, thì
bên ngoài có quyền truy xuất đến các thành phần này thông qua đối tượng
của lớp dẫn xuất hay không.
29
4.1. ĐƠN KẾ THỪA
Phạm vi truy xuất
• Truy xuất các thành phần của lớp cơ sở từ lớp dẫn xuất
- Thành phần public: có thể được truy xuất từ bất cứ nơi nào (từ sau khai
báo lớp).
- Thành phần private: chỉ được truy xuất bởi các hàm thành phần và các
hàm friend của lớp cơ sở. <“Phạm vi lớp” chỉ mở rộng cho “bạn bè”, mà
không được mở rộng đến các lớp “con cháu”>
- Thành phần protected: Thành phần kiểu protected giống như private,
ngoại trừ một điều là các thành phần này có thể được truy nhập từ lớp dẫn
xuất. <Đặc biệt cần chú ý là các thành phần protected không thể truy nhập
từ bên ngoài lớp cơ sở hay lớp dẫn xuất từ lớp cơ sở đó>.
30
4.1. ĐƠN KẾ THỪA
Phạm vi truy xuất
• Nhận xét:
- Thuộc tính protected là phương tiện để tránh phải sửa đổi lớp cơ sở khi
có lớp dẫn xuất mới ra đời. Nhờ đó nó bảo đảm được tính đóng của một
lớp. Khai báo một thành phần nào có thuộc tính protected tương đương với
qui định trước tất cả các lớp con, cháu sau này đều là bạn của thành phần
đó.
- Thông thường ta dùng thuộc tính protected cho các thành phần dữ liệu
và thuộc tính public cho hàm thành phần.
31
4.1. ĐƠN KẾ THỪA
Phạm vi truy xuất
• Tổng kết khả năng truy nhập các thành phần trong một lớp:
• Khả năng truy nhập các thành phần trong một lớp:
33
4.1. ĐƠN KẾ THỪA
Các kiểu kế thừa
35
4.1. ĐƠN KẾ THỪA
- Cách cài đặt một số hàm đặc biệt trong lớp như hàm tạo, hàm
tạo sao chép, hàm hủy,….
37
CÂU HỎI CUỐI CHƯƠNG
Câu 1: Giải thích sự khác nhau giữa các thành phần protected và private?
Câu 2: Hàm tạo và hàm hủy hoạt động như thế nào trong lớp kế thừa?
Câu 4: Tìm lỗi sai của đoạn chương trình sau đây
class A
{ public:
void func();
};
class B: private A
{};
main()
{ A a;
B b;
a.func();
b.func();
A *pA= &b;
B *pb= &a;
38
}
BÀI TẬP
Bài tập 1: Xây dựng lớp Người gồm có các thành phần Họ tên và Năm
sinh. Viết các phương thức của lớp như nhập, hiển thị dữ liệu.
Xây dựng lớp Thí sinh kế thừa lớp này, trong lớp Thí sinh có Số báo
danh và Điểm các môn toán, lý, hóa. Hãy xây dựng thêm các phương thức
nhập, xuất và khai báo mảng có kiểu là Thisinh để quản lý danh sách các thí
sinh. Viết chương trình với các công việc sau:
- Hiển thị danh sách thí sinh có tổng điểm ba môn tăng dần;
- Thống kê xem có bao nhiêu phần trăm thí sinh đạt yêu cầu (cả ba môn
có điểm lớn hơn hoặc bằng 5). 39
BÀI TẬP
Bài tập 2:
Xây dựng lớp Sản phẩm bao gồm: Mã sản phẩm, Tên sản
phẩm, Năm sản xuất và các phương thức nhập, xuất.
Xây dựng tiếp lớp Tivi có thêm các thuộc tính Chiều dài, Chiều
rộng của màn hình. Hãy xây dựng phương thức tính diện tích của
màn hình của lớp Tivi.
Viết chương trình nhập vào các đối tượng của lớp Tivi, sau đó
sắp xếp tăng dần theo diện tích màn hình của các đối tượng này
40
ĐẠI HỌC KINH TẾ KỸ THUẬT CÔNG NGHIỆP
KHOA CÔNG NGHỆ THÔNG TIN
4.2 Đa thừa kế
4.3 Các lớp cơ sở ảo
Năm học 2021 - 2022 41
4.2 ĐA THỪA KẾ
Khái niệm:
Đa kế thừa là cơ
chế, trong đó lớp dẫn
xuất có thể kế thừa
từ hai hay nhiều lớp
cơ sở
Về nguyên tắc,
những gì đã đề cập
trong phần đơn kế
thừa được tổng quát
hoá cho trường hợp
đa kế thừa.
42
4.2 ĐA THỪA KẾ
Cú pháp của một lớp kế thừa nhiều lớp cơ sở:
43
4.2 ĐA THỪA KẾ
Trong đa kế thừa, mỗi lớp cơ sở được phân cách nhau
bởi dấu phẩy “,”.
Mỗi lớp cơ sở có thể có một kiểu dẫn xuất bởi một từ
khoá dẫn xuất khác nhau.
Nguyên tắc truy nhập vào các thành phần lớp cơ sở
cũng hoàn toàn tương tự như trong kế thừa đơn.
44
4.2 ĐA THỪA KẾ
Ví dụ: lớp Der kế thừa từ hai lớp Base1, Base2
- Các hàm thiết lập và huỷ bỏ được định nghĩa và gọi như thế nào: thứ tự,
truyền thông tin v.v.?
- Làm thế nào giải quyết tình trạng kế thừa xung đột trong đó, lớp D dẫn
xuất từ B và C, và cả hai cùng là dẫn xuất của A.
46
4.2 ĐA THỪA KẾ
Thứ tự gọi các hàm tạo, hàm hủy:
• Thứ tự gọi các hàm tạo: Các hàm tạo của các lớp cơ sở theo thứ tự
khai báo của các lớp cơ sở trong lớp kế thừa lần lượt được gọi, sau cùng là
hàm tạo của lớp kế thừa mới được gọi. <Trong trường hợp ví dụ trên, hàm
tạo lớp Base1 được gọi trước, sau đó đến hàm tạo lớp Base2 va cuối cùng
là hàm tạo lớp Der được gọi>
• Thứ tự gọi các hàm hủy: Các hàm hủy được gọi theo thứ tự ngược lại
với cách gọi hàm tạo. <Trong ví dụ trên hàm hủy lớp Der được gọi trước, sau
đó đến hàm hủy lớp Base2, cuối cùng là hàm hủy lớp Base1 được gọi.>
47
4.2 ĐA THỪA KẾ
• Truy nhập thành phần lớp cơ sở: Giống như trong đơn kế thừa, trong
hàm thành phần của lớp dẫn xuất có thể sử dụng tất cả các hàm thành phần
public (hoặc protected) của lớp cơ sở.
- Việc truy nhập từ đối tượng lớp dẫn xuất đến các thành phần của mỗi
lớp cơ sở được tuân theo quy tắc phạm vi tương tự như trong đơn kế thừa.
- Trong trường hợp các lớp cơ sở đều có các thành phần cùng tên, việc
truy xuất đến thành phần của lớp nào phải được chỉ rõ bằng toán tử phạm vi:
“<Tên lớp>::” đối với thành phần lớp cơ sở đó.
48
4.2 ĐA THỪA KẾ
Toán tử phạm vi :: được sử dụng trong trường hợp trên để tránh sự nhập
nhằng, ví dụ:
Trong lớp Base1 có hàm show(), trong lớp Base2 cũng có hàm show().
Vậy trong lớp Der sẽ có 2 hàm show() của hai lớp cơ sở. Khi ở lớp Der dùng
hàm show() nào cần chỉ rõ phạm vi lớp của hàm show() đó. Chẳng hạn:
50
4.2 ĐA THỪA KẾ
Chú ý: Nếu một lớp cơ sở định nghĩa một hàm tạo không tham
số (hàm mặc định) hoặc định nghĩa một hàm tạo mà mọi tham số
có giá trị ngầm định thì khi viết hàm tạo lớp dẫn xuất không nhất
thiết phải gọi lại hàm tạo này.
51
4.2 ĐA THỪA KẾ
class A Hàm tạo của lớp C kế thừa từ cơ sở A, B bắt buộc
{ phải gọi lại hàm tạo của lớp A nhưng không nhất
public: thiết phải gọi lại hàm tạo của lớp B. Lớp C và hàm
int x1; main() có thể được viết như sau:
A(int x)
{ class C: public A, public B
x1=x; { public:
}
}; int x3;
class B C(int a,int c): A(a)
{
public: {x3=c;}
int x2; };
B(int main()
x=0)
{ {C m(4,8);
x2=x;
} cout<<m.x1<<”“<<m.x2<<” “<<m.x3;
}; } 52
4.3 CÁC LỚP CƠ SỞ ẢO
Trong đa kế thừa, thường gặp tình huống một lớp cơ sở có nhiều
lớp kế thừa trực tiếp.
class A
{ ………
class B: public A{ }
class C: public A{ }
Để xử lý vấn đề trên, C++ cho phép chỉ tổ hợp một lần duy nhất các
thành phần của lớp A trong lớp D nhờ việc khai báo trong các lớp B, C
rằng A là lớp cơ sở ảo.
54
4.3 CÁC LỚP CƠ SỞ ẢO
Khai báo lớp cơ sở ảo
Trong khai báo lớp dẫn xuất bổ sung trước tên lớp kế thừa từ khóa virtual.
class A
{ ………..
int v1; ……………
}
Thứ tự gọi các hàm tạo của các lớp cơ sở được xác định theo nguyên tắc:
- Đầu tiên các hàm tạo của các lớp cơ sở ảo sẽ được gọi theo thứ tự xuất
hiện của chúng khi khai báo lớp dẫn xuất.
- Sau khi các hàm tạo của lớp cơ sở ảo được gọi thì hàm tạo của các lớp
cơ sở khác sẽ được gọi. Thứ tự thực hiện của các hàm tạo này cũng được quyết
định bởi thứ tự khai báo của các lớp đó trong lớp dẫn xuất.
Thứ tự gọi các hàm hủy ngược lại so với hàm tạo.
56
4.3 CÁC LỚP CƠ SỞ ẢO
Cài đặt CT <Code Ví dụ 4.17 Tài liệu học tập>
57
4.3 CÁC LỚP CƠ SỞ ẢO – TÓM TẮT NỘI DUNG
Các nội dung chính:
Các lớp cơ sở ảo
58
TÓM TẮT NỘI DUNG
Các nội dung chính:
Các lớp cơ sở ảo
59
CÂU HỎI VÀ BÀI TẬP
Câu 1: Chỉ ra cách khai báo đối tượng có thể có mà không thực hiện hàm tạo
sao chép cho lớp dẫn xuất A2, B2, C2 dưới đây.
class A1
{
private:
A1(int i);
public:
A1(int i, int j)
};
class A2 : public A1
{
public:
A2() {}
A2(int i) : A1(i) {}
A2(int i, int j): A1(i,j) {}
};
60
CÂU HỎI VÀ BÀI TẬP
class B1
{ protected:
B1();};
class B2 : public B1
{ };
class C1
{
public:
virtual void func ()=0;
};
class C2 : public C1
{
}; 61
CÂU HỎI VÀ BÀI TẬP
Câu 2: Tìm lỗi sai của đoạn chương trình sau đây
class A{
public:
void func();
};
class B: private A
{};
main()
{ A a;
B b;
a.func();
b.func();
A *pA= &b;
B *pb= &a;
}
62
CÂU HỎI VÀ BÀI TẬP
Bài tập: Xây dựng một lớp đối tượng các máy in, lớp gồm có các thành
phần:
- Các thuộc tính số hiệu và số lượng trong kho.
- Hàm nhapkho(int q) để nhập vào kho q đơn vị mặt hàng.
- Hàm xuatkho(int q) để xuất ra khỏi kho q đơn vị mặt hàng.
Xây dựng lớp máy in laser kế thừa từ lớp máy in và có thêm thuộc tính dpi
Xây dựng lớp máy in màu kế thừa từ lớp máy in có thêm thuộc tính bảng màu.
Xây dựng lớp máy in laser màu kế thừa từ lớp máy in laser và máy in màu.
Viết chương trình hướng đối tượng quản lý các loại máy in với các thủ tục:
nhập, xuất và in ra số lượng các loại có trong kho.
63