Kiểu kế thừa: private // error // error Do lớp B kế thừa kiểu private nên tính truy cập của các thành viên đƣợc kế thừa từ lớp A sẽ chuyển thành private , mà private chỉ đƣợc phép tru
Trang 1Số tiết lý thuyết: 45 tiết
Số tiết thực hành: 30 tiết
Trang 2 Chương 1: Tổng quan về OOP
Chương 2: Lớp & đối tƣợng
Chương 3: Hàm và hàm đa năng trong OOP
Chương 4: Đa năng hóa toán tử
Chương 5: Sự kế thừa và tính đa hình
Nội dung môn học:
Trang 3 Khái niệm sự kế thừa
Chương 4: Sự kế thừa và tính đa hình
Trang 4 Khái niệm sự kế thừa
Trang 5NV_VANPHONG
LCB Phucap
NV_SANXUAT
Sogiolam SoSP
NHANVIEN
MaNV Hoten CMND Nhap() Xuat() Tinhluong()
MaNV Hoten CMND Nhap() Xuat()
MaNV Hoten CMND Nhap() Xuat()
KẾ THỪA
NHÂN
VIÊN
Trang 6 Khái niệm sự kế thừa
Trang 7 Khái niệm sự kế thừa
Lớp A Lớp B
Lớp cơ bản
Lớp dẫn xuất
từ A
Lớp A Lớp B Lớp C Lớp dẫn xuất từ B
Lớp A Lớp B
Lớp C Lớp B Lớp C
Lớp D Lớp A Lớp A
Trang 8 Khái niệm sự kế thừa
Chương 4: Sự kế thừa và tính đa hình
Trang 9Lớp A Lớp B
Lớp cơ bản
Lớp dẫn xuất
từ A
Lớp A Lớp B
Trang 10class Danxuat: <từ khóa chỉ định kiểu kế thừa> Coban{
… };
class HINH{
private: int mau;
};
class HCN:public HINH{
private: int dai,rong;
};
Trang 11 Kiểu kế thừa protected: một lớp kế thừa kiểu protected làm
thay đổi tính truy cập các thành viên của lớp cơ bản thành
protected
Kiểu kế thừa private: một lớp kế thừa kiểu private làm thay
đổi tính truy cập các thành viên của lớp cơ bản thành
private
LỚP
CƠ BẢN
THỪA KẾ PUBLIC
THỪA KẾ PRIVATE
THỪA KẾ PROTECTED
PUBLIC PROTECTED PRIVATE
PUBLIC PROTECTED
NO
PRIVATE PRIVATE
NO
PROTECTED PROTECTED
NO
Trang 12 Kiểu kế thừa: public
Trang 13 Kiểu kế thừa: private
// error // error
Do lớp B kế thừa kiểu private nên tính truy cập của các thành viên đƣợc
kế thừa từ lớp A sẽ chuyển thành
private , mà private chỉ đƣợc phép truy xuất bên trong lớp
Trang 14 Kiểu kế thừa: protected
Do lớp B kế thừa kiểu protected
nên tính truy cập của các thành viên đƣợc kế thừa từ lớp A sẽ chuyển thành protected, mà
protected chỉ đƣợc phép truy xuất bên trong lớp.và lớp dẫn xuất
Trang 15 Trình tự gọi hàm khởi tạo:
Khi có kế thừa, trình tự gọi hàm khởi tạo thực hiện theo nguyên tắc: hàm khởi tạo của lớp cơ bản được gọi trước và
lớp dẫn xuất gọi sau
Lưu ý: ta có thể chỉ định hàm khởi tạo nào của lớp cơ bản được
gọi bằng toán tử “:” (xem lại chương 1)
B():A(){ }; // gọi hàm A()
B(int c):A(c){ }; // gọi hàm A(int)
};
Trang 16 Gọi hàm thành viên trong kế thừa:
Hàm thành viên của lớp dẫn xuất có thể cùng tên với tên hàm thành viên của lớp cơ bản
Hàm thành viên đƣợc gọi ứng với đối tƣợng gọi nó
<Tên lớp cơ bàn>::<Tên hàm>(…)
Con trỏ trong kế thừa:
Con trỏ lớp cơ bản có thể trỏ tới địa chỉ đối tƣợng của lớp dẫn xuất nhƣng ngược lại thì không
Đối tƣợng lớp dẫn xuất đƣợc xem nhƣ là một đối tƣợng của lớp cơ bản khi xử lý qua con trỏ nhƣng ngược lại thì không
Trang 17 Ví dụ trình tự gọi hàm khởi tạo và hàm huỷ trong kế thừa đơn:
base 2 derived 2 Huy derived
Trang 18 Ví dụ gọi hàm hàm thành viên trong kế thừa:
Cho biết kết quả khi thực thi hàm main() sau:
Y/C: Sửa lại lớp derived để hàm main() xuất ra chữ hello:
Trang 19 Ví dụ con trỏ trong kế thừa:
Con trỏ lớp dẫn xuất KHÔNG đƣợc phép
tham chiếu đến đối tƣợng lớp cơ bản
Trang 20 Khái niệm sự kế thừa
Chương 4: Sự kế thừa và tính đa hình
Trang 21Kế thừa từ các lớp khác nhau
Kế thừa từ một lớp cơ
bản chung
Trang 22Lưu ý: Quy tắc về tính truy cập và kiểu thừa
kế trong đa kế thừa cũng giống nhƣ trong kế thừa đơn
class A{
… };
class B{
… };
class C: <từ khóa chỉ định kiểu kế thừa> A, <từ khóa chỉ định kiểu kế thừa> B [, ]{
… };
Trang 24 Đặc điểm đa kế thừa:
Trình tự gọi hàm khởi tạo:
Khi có đa kế thừa, trình tự gọi hàm khởi tạo thực hiện theo nguyên tắc sau:
Hàm khởi tạo của lớp cơ bản được gọi trước theo
trình tự từ trái sang phải (liệt kê trong kế thừa)
Nếu trong lớp có chứa các đối tượng là thành viên dữ liệu của lớp thì nó sẽ đƣợc khởi tạo tiếp theo
Gọi hàm khởi tạo của lớp dẫn xuất
Trình tự gọi hàm huỷ:
Hàm hủy đƣợc gọi theo trình tự ngƣợc lại:
Hàm hủy của lớp dẫn xuất
Hàm hủy của các đối tƣợng
Hàm hủy của lớp cơ bản
Ví dụ:
Trang 25 Ví dụ 1: trình tự gọi hàm khởi tạo và hàm huỷ trong đa kế thừa
class C: public A, public B{
public:
C(){cout<<"C"<<endl;}
~C(){cout<<"Huy C"<<endl;}
Trang 26 Ví dụ 2: trình tự gọi hàm khởi tạo và hàm huỷ trong đa kế thừa khi lớp có chứa các đối tượng là thành viên dữ liệu của lớp
class C: public A, public B{
Trang 27 Vấn đề trong đa kế thừa:
Do lớp dẫn xuất thừa kế các thành viên dữ liệu và hàm thành viên từ nhiều lớp nên ta cần phải giải quyết vấn đề trùng lặp
trong đa kế thừa (lỗi mơ hồ: ambiguous)
Ví dụ:
GIAOVIEN
Hoten Luong Nhap(),Xuat()
Kế thừa từ lớp nào?
SINHVIEN
Hoten Lop Nhap(),Xuat()
TROGIANG Hoten ???
Luong, Lop Nhap(),Xuat()
Trang 28 Vấn đề trong đa kế thừa:
Để giải quyết vấn đề trùng lắp dữ liệu trong đa kế thừa,ta thực hiện một trong các phương pháp sau:
Cách 1: dùng toán tử phạm vi “::” chỉ rõ thành viên dữ liệu hay hàm của lớp nào đƣợc gọi
Cách 2: định nghĩa một hàm cụ thể trong lớp dẫn xuất
Cách 3: nếu kế thừa từ một lớp cơ bản chung thì ta có thể
dùng lớp cơ bản ảo cho việc kế thừa của lớp dẫn xuất theo
cú pháp sau:
Lớp B Lớp C Lớp D Lớp A Lớp A
Lớp B Lớp C Lớp D Lớp A
Trang 29 Cách 1: dùng toán tử phạm vi “::” chỉ rõ thành viên dữ liệu hay hàm của lớp nào đƣợc gọi
class person{
public: char name[20];
public:
person(){strcpy(name, “chua biet");}
void out(){cout<<"chao "<<name<<endl;}
Trang 30 Cách 2: định nghĩa một hàm cụ thể trong lớp dẫn xuất
class person{
public: char name[20];
public:
person(){strcpy(name, “chua biet");}
void out(){cout<<"chao "<<name<<endl;}
Trang 31 Cách 3: sử dụng lớp cơ bản ảo trong kế thừa
class person{
public: char name[20];
public:
person(){strcpy(name, “chua biet");}
void out(){cout<<"chao "<<name<<endl;}
void out(){cout<<"hello "<<name; }
Cho biết kết quả của CT sau:
Trang 32 Cách 3: sử dụng lớp cơ bản ảo trong kế thừa
Lợi ích của việc dùng lớp cơ bản ảo khi có kế thừa từ
lớp cơ bản chung:
– Tránh việc gọi hàm khởi tạo của lớp cơ bản nhiều
lần từ lớp dẫn xuất
– Tránh việc trùng lắp các bản sao thành phần dữ
liệu của lớp cơ bản
– Trình tự hàm khởi tạo và hàm hủy khi dùng lớp cơ
bản ảo:
• Hàm khởi tạo của lớp cơ bản ảo được gọi trước
tiên (nếu có nhiều lớp cơ bản ảo thì trình tự bắt đầu từ trên xuống, từ trái sang phải theo đúng trình tự trong cây thư mục kế thừa
• Hàm hủy được gọi theo trình tự ngược lại
Trang 33 Khái niệm sự kế thừa
Chương 4: Sự kế thừa và tính đa hình
Trang 34 Tính đa hình trong kế thừa
• Hoặc cùng là đối tƣợng sinh viên nhƣng sinh viên chính qui có thời lƣợng học khác với thời lƣợng học của sinh viên từ xa
Trang 35 Tính đa hình trong kế thừa
Khái niệm
Ví dụ
Cú pháp
Cú pháp:
Cách thể hiện tính đa hình trong OOP:
Hàm đa năng là một cách thể hiện tính đa hình trong OOP
Dùng hàm ảo theo cú pháp khai báo sau: dùng từ khóa virtual trước kiểu dữ liệu trả về của hàm:
virtual <Kiểu_data> Tên_hàm(DS_đối số);
Lưu ý:
• Hàm ảo khi chỉ có khai báo mà không có định nghĩa ngay
trong lớp thì được gọi là hàm thuần ảo (pure virtual)
Cú pháp khai báo như sau:
virtual <Kiểu_data> Tên_hàm(DS_đối số) = 0;
• Một lớp nếu có chứa hàm thuần ảo thì được gọi là lớp trừu tượng (abstract class) Lớp trừu tượng là lớp không
Trang 36 Ví dụ 1: Cho biết kết quả của chương trình sau
Trang 37 Khắc phục: Để kết quả của chương trình Ví dụ 1 thực thi đúng:
Con trỏ p trỏ vào đối tượng là hinhtron thì dùng hàm ve() của lớp hinhtron
Con trỏ p trỏ vào đối tượng là hinhchunhat thì
Trang 40Câu 1: Cho biết kết quả của đoạn chương trình sau:
Trang 41Câu 2: Cho biết kết quả của đoạn chương trình sau:
}
};
A Chương trình báo lỗi
B Kết quả in ra: ABC
C Kết quả in ra: ABB
D Kết quả in ra: BBC
void main() {
Trang 42Câu 3: Cho biết kết quả của đoạn chương trình sau:
Trang 43Câu 4: Lớp D là lớp kế thừa public từ lớp B Hàm F là hàm
friend của lớp D Khi đó:
A Hàm F cũng là hàm bạn của lớp B
B Hàm F có thể truy xuất các thành viên có giới hạn private của lớp B
C Hàm F có thể truy xuất các thành viên có giới hạn private của lớp B và D
D Hàm F có thể truy xuất các thành viên có giới hạn private của lớp D
Trang 44Câu 5: Phát biểu nào sau đây là SAI:
A Lớp dẫn xuất có thể khởi tạo giá trị khác cho các thuộc tính có giới hạn public của
lớp cơ sở
B Destructor của lớp cơ sở được gọi sau destructor của lớp dẫn xuất
C Nếu một lớp dẫn xuất không có cài đặt hàm destructor của lớp cơ sở cũng sẽ
không được gọi khi giải phóng đối tượng thuộc lớp dẫn xuất
D Constructor của lớp cơ sở được gọi trước constructor của lớp dẫn xuất
Trang 45Câu 6: Phát biểu nào sau đây là SAI:
A Hàm ảo của lớp phải đƣợc cài đặt trong lớp đó
B Hàm ảo trong lớp cơ sở có thể đƣợc viết lại trong lớp dẫn xuất
C Hàm ảo khác với hàm thuần ảo
D Hàm ảo của một lớp có thể là hàm friend của lớp đó
Trang 46Câu 7: Sự khác nhau giữa hàm ảo (virtual) và hàm thuần ảo
(pure virtual):
A Hàm thuần ảo không có phần cài đặt
B Hàm thuần ảo phải có khai báo từ khoá pure virutal
C Hàm ảo và hàm thuần ảo là giống nhau
D Một lớp hoặc chỉ có hàm thuần ảo hoặc chỉ có hàm ảo
Trang 47Câu 8: Cho biết kết quả của đoạn chương trình sau:
C Chương trình báo lỗi
D Màn hình xuất hiện: A=5
Trang 48Câu 9: Cho biết kết quả của đoạn chương trình sau:
A Chương trình chạy và hiển thị ký tự C trên màn hình
B Chương trình chạy và không hiển thị kết quả gì
C Chương trình báo lỗi do khai báo biến con trỏ pb thuộc kiểu lớp trừu tượng
D Chương trình báo lỗi do khai báo biến c thuộc kiểu lớp trừu tượng
Trang 49Câu 10: Cho biết kết quả của đoạn chương trình sau:
class Base{
public:
Base(){ cout<<"Base class " <<endl;}
};
class Derive: Base {
Derive(){ cout<<"Derive class " <<endl;}
A Base class Derive class
B Derive class Base class
C Base class Base class Derive class
D Base class Derive class Base class
Trang 51ĐÁP ÁN:
1 B Do obj là con trỏ thuộc lớp a nên sẽ sủ dụng hàm func() của lớp a
2 B Câu lệnh pb ->A::In() cho biết con trỏ pb cần gọi hàm In() của lớp A
3 D x: thuộc phạm vi public nên kế thừa, y: thuộc protected nên cũng đƣợc kế thừa, z là thành phần thuộc lớp C nên đƣợc sử dụng
Đáp án A sai vì hàm bạn không có tính chất bắc cầu hàm F không liên quan tới lớp B B,C sai do hàm F không thể truy xuất
dữ liệu thuộc private của B
5 C Hàm huỷ đƣợc gọi một cách tự động trong kế thừa mà không phụ thuộc vào việc có cài đặt hay không?
Trang 52ĐÁP ÁN:
6 D Hàm ảo phải là hàm thành viên của lớp
7 A Các khái niệm B,D,C là những khái niệm SAI
8 D Câu lệnh d.BaseA::Print(); đã xác định rõ hàm Print() là của lớp A
9 A pb là con trỏ lớp cơ bản nên có thể trỏ tới đối tượng là lớp dẫn xuất nhưng ngược lại thì không
Derive d;
d là đối tượng lớp dẫn xuất nên trỉnh tự gọi hàm khởi tạo: lớp cơ bản gọi trước, lớp dẫn xuất gọi sau