Chương 4: Sự kế thừa và tính đa hình

 Khái niệm: Kế thừa trong OOP là sự tái sử dụng các lớp có các đặc tính chung với nhau để tạo ra các lớp mới từ một hay nhiều lớp đã có.  Ví dụ: Xét về bản chất: NV_VANPHONG và NV_SANXUAT đều là nhân viên nên nó phải có các thuộc tính chung: MaNV, Hoten, CMND. của một ngƣởi nhân viên.

pdf52 trang | Chia sẻ: lylyngoc | Lượt xem: 1504 | Lượt tải: 2download
Bạn đang xem trước 20 trang tài liệu Chương 4: Sự kế thừa và tính đa hình, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
ĐH CÔNG NGHỆ THÔNG TIN Số tiết lý thuyết: 45 tiết Số tiết thực hành: 30 tiết 1 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: 3 Khái niệm sự kế thừa Kế thừa đơn Đa kế thừa  Tính đa hình trong kế thừa  Chương 4: Sự kế thừa và tính đa hình 4 Khái niệm sự kế thừa  Chương 4: Sự kế thừa và tính đa hình  Khái niệm  Ví dụ  Ƣu điểm  Thành phần kế thừa  Phân loại  Khái niệm: Kế thừa trong OOP là sự tái sử dụng các lớp có các đặc tính chung với nhau để tạo ra các lớp mới từ một hay nhiều lớp đã có. Ví dụ: Xét về bản chất: NV_VANPHONG và NV_SANXUAT đều là nhân viên nên nó phải có các thuộc tính chung: MaNV, Hoten, CMND.. của một ngƣởi nhân viên. 5 NV_VANPHONG LCB Phucap NV_SANXUAT Sogiolam SoSP NHANVIEN MaNV Hoten CMND Nhap() Xuat() Tinhluong() MaNV Hoten CMND Nhap() Xuat() Tinhluong() MaNV Hoten CMND Nhap() Xuat() Tinhluong() KẾ THỪA NHÂN VIÊN  Chương 4: Sự kế thừa và tính đa hình 6 Khái niệm sự kế thừa  Khái niệm  Ví dụ  Ƣu điểm  Thành phần kế thừa  Phân loại  Ƣu điểm:  Tái sử dụng chƣơng trình đã có  Cho phép tạo ra các thƣ việc lớp (là tập hợp dữ liệu và hàm đƣợc đóng gói thành các lớp, ví dụ: thƣ viện math.h, string.h…)  Thành phần kế thừa: Lớp kế thừa sẽ kế thừa:  Thành phần dữ liệu không thuộc private của lớp đƣợc kế thừa.  Đƣợc quyền truy xuất các hàm thành viên không thuộc private của lớp đƣợc kế thừa.  Chương 4: Sự kế thừa và tính đa hình 7 Khái niệm sự kế thừa  Khái niệm  Ví dụ  Ƣu điểm  Thành phần kế thừa  Phân loại  Phân loại:  Kế thừa đơn:  Đa kế thừa: Lớp cơ bản Lớp dẫn xuất 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  Chương 4: Sự kế thừa và tính đa hình 8 Khái niệm sự kế thừa Kế thừa đơn Đa kế thừa  Tính đa hình trong kế thừa  Chương 4: Sự kế thừa và tính đa hình 9 Kế thừa đơn  Khái niệm  Phân loại  Cú pháp  Kiểu kế thừa  Ví dụ  Đặc điểm  Khái niệm: Kế thừa đơn là tiến trình tạo ra một lớp mới từ một lớp đã có.  Phân loại:  Chương 4: Sự kế thừa và tính đa hình Lớp cơ bản Lớp dẫn xuất 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 Kế thừa đơn 1 cấp Kế thừa đơn nhiều cấp (đa tầng) Lưu ý: trong kế thừa đơn nhiều cấp ta cần phân biệt lớp cơ bản trực tiếp (Lớp A) và lớp cơ bản gián tiếp (lớp B) => Lớp cơ bản trực tiếp: là lớp có tên trong khai báo của lớp dẫn xuất. 10 Kế thừa đơn  Khái niệm  Phân loại  Cú pháp  Kiểu kế thừa  Ví dụ  Đặc điểm  Cú pháp: Trong đó: : có thể là public,protected hoặc private Lưu ý: khi không có từ khoá chỉ định thì mặc định kiểu kế thừa là private Ví dụ:  Chương 4: Sự kế thừa và tính đa hình class Coban{ … }; class Danxuat: Coban{ … }; class HINH{ private: int mau; }; class HCN:public HINH{ private: int dai,rong; }; 11 Kế thừa đơn  Khái niệm  Phân loại  Cú pháp  Kiểu kế thừa  Ví dụ  Đặc điểm  Kiểu kế thừa:  Kiểu kế thừa public: một lớp kế thừa kiểu public không làm thay đổi tính truy cập các thành viên của lớp cơ bản.  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.  Ví dụ:  Chương 4: Sự kế thừa và tính đa hình 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 12  Kiểu kế thừa: public  Chương 4: Sự kế thừa và tính đa hình class A{ private: int data_pri; public: int data_pub; }; class B: public A{ void output(){ cout<<data_pri; cout<<data_pub; } }; class C: public B{ void output(){ cout<<data_pri; cout<<data_pub; } }; void main{ B obj; obj.data_pri=5; obj.data_pub=6; } // error // ok // error // ok // error // ok 13  Kiểu kế thừa: private  Chương 4: Sự kế thừa và tính đa hình class A{ private: int data_pri; public: int data_pub; }; class B: private A{ void output(){ cout<<data_pri; cout<<data_pub; } }; class C: public B{ void output(){ cout<<data_pri; cout<<data_pub; } }; void main{ B obj; obj.data_pri=5; obj.data_pub=6; } // error // ok // error // error // 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. 14  Kiểu kế thừa: protected  Chương 4: Sự kế thừa và tính đa hình class A{ private: int data_pri; public: int data_pub; }; class B: protected A{ void output(){ cout<<data_pri; cout<<data_pub; } }; class C: public B{ void output(){ cout<<data_pri; cout<<data_pub; } }; void main{ B obj; obj.data_pri=5; obj.data_pub=6; } // error // ok // error // ok // error // error 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 15 Kế thừa đơn  Khái niệm  Phân loại  Cú pháp  Kiểu kế thừa  Ví dụ  Đặc điểm Đặc điểm:  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). Ví dụ:  Trình tự gọi hàm huỷ:  Hàm hủy đƣợc gọi theo trình tự ngƣợc lại: lớp dẫn xuất đƣợc gọi trƣớc và lớp cơ bản gọi sau.  Chương 4: Sự kế thừa và tính đa hình class A{ A(); A(int); }; class B:public A{ B():A(){..}; // gọi hàm A() B(int c):A(c){..}; // gọi hàm A(int) }; 16 Kế thừa đơn  Khái niệm  Phân loại  Cú pháp  Kiểu kế thừa  Ví dụ  Đặc điểm Đặc điểm:  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ó Ví dụ:  Đối tƣợng lớp cơ bản gọi thì hàm thành viên của lớp cơ bản thực hiện.  Đối tƣợng lớp dẫn xuất gọi thì hàm thành viên của lớp dẫn xuất thực hiện.  Trong lớp dẫn xuất, muốn gọi hàm thành viên cùng tên của lớp cơ bản, ta dùng toán tử phạm vi “::” theo cú pháp sau: ::(…)  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.  Chương 4: Sự kế thừa và tính đa hình 17  Ví dụ trình tự gọi hàm khởi tạo và hàm huỷ trong kế thừa đơn:  Chương 4: Sự kế thừa và tính đa hình class base{ public: base(){cout<<“base 1”;} base(int){cout<<“base 2”;} ~base(){cout<<“Huy base”;} }; class derived: public base{ public: derived():base(){cout<<“derived 1”;} derived(int):base(5){cout<<“derived 2”;} ~derived(){cout<<“Huy derived”;} }; void main(){ derived d; } void main(){ derived d(2); } base 1 derived 1 Huy derived Huy base base 2 derived 2 Huy derived Huy base 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:  Chương 4: Sự kế thừa và tính đa hình class base{ public: void out(){cout<<"Hello";} }; class derived: public base{ public: void out(){cout<<"Chao";} void show(){ out(); } }; void main(){ derived d; d.show(); } class derived: public base{ public: void out(){cout<<"Chao";} void show(){ base::out(); } }; Chao Hello 19  Ví dụ con trỏ trong kế thừa:  Chương 4: Sự kế thừa và tính đa hình class A{ protected: int dataA; public: A(){dataA=5;} void showA(){cout<<dataA;} }; class B: public A{ public: int dataB; public: B():A(){dataB=10;} void showB(){cout<<dataB;} }; void main(){ A a; B *p; p=&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. void main(){ A *p; B b; p=&b; } Con trỏ lớp cơ bản ĐƢỢC phép tham chiếu đến đối tƣợng lớp dẫn xuất.   20 Khái niệm sự kế thừa Kế thừa đơn Đa kế thừa  Tính đa hình trong kế thừa  Chương 4: Sự kế thừa và tính đa hình 21 Đa kế thừa  Khái niệm  Phân loại  Cú pháp  Ví dụ  Đặc điểm  Vấn đề  Khái niệm: Đa kế thừa là tiến trình tạo ra một lớp mới từ nhiều lớp đã có.  Phân loại:  Chương 4: Sự kế thừa và tính đa hình Lớp A Lớp B Lớp C Lớp B Lớp C Lớp D Lớp A Lớp A Kế thừa từ các lớp khác nhau Kế thừa từ một lớp cơ bản chung 22 Đa kế thừa  Khái niệm  Phân loại  Cú pháp  Ví dụ  Đặc điểm  Vấn đề  Cú pháp: Lư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.  Chương 4: Sự kế thừa và tính đa hình class A{ … }; class B{ … }; class C: A, <từ khóa chỉ định kiểu kế thừa> B [,..]{ … }; 23 Đa kế thừa  Khái niệm  Phân loại  Cú pháp  Ví dụ  Đặc điểm  Vấn đề Ví dụ:  Chương 4: Sự kế thừa và tính đa hình class GIAOVIEN{ protected: char hoten[40]; int luong; public: void nhap(); void xuat(); }; class SINHVIEN{ protected: char hoten[40]; int luong; public: void nhap(); void xuat(); }; class TROGIANG: public GIAOVIEN, public SINHVIEN { … }; 24 Đa kế thừa  Khái niệm  Phân loại  Cú pháp  Ví dụ  Đặc điểm  Vấn đề Đặ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ụ:  Chương 4: Sự kế thừa và tính đa hình 25  Ví dụ 1: trình tự gọi hàm khởi tạo và hàm huỷ trong đa kế thừa  Chương 4: Sự kế thừa và tính đa hình class A{ public: A(){cout<<"A“<<endl;} ~A(){cout<<"Huy A"<<endl;} }; class B{ public: B(){cout<<"B"<<endl;} ~B(){cout<<"Huy B"<<endl;} }; Cho biết kết quả của CT sau: void main(){ C c; } A B C Huy C Huy B Huy A class C: public A, public B{ public: C(){cout<<"C"<<endl;} ~C(){cout<<"Huy C"<<endl;} }; 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  Chương 4: Sự kế thừa và tính đa hình class A{ public: A(){cout<<"A“<<endl;} ~A(){cout<<"Huy A"<<endl;} }; class B{ public: B(){cout<<"B"<<endl;} ~B(){cout<<"Huy B"<<endl;} }; Cho biết kết quả của CT sau: void main(){ C c; } A B A C Huy C Huy A Huy B Huy A class C: public A, public B{ private: A a; public: C(){cout<<"C"<<endl;} ~C(){cout<<"Huy C"<<endl;} }; 27 Đa kế thừa  Khái niệm  Phân loại  Cú pháp  Ví dụ  Đặc điểm  Vấn đề 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ụ:  Chương 4: Sự kế thừa và tính đa hình GIAOVIEN Hoten Luong Nhap(),Xuat() Kế thừa từ lớp nào? SINHVIEN Hoten Lop Nhap(),Xuat() TROGIANG Hoten ??? Luong, Lop Nhap(),Xuat() 28 Đa kế thừa  Khái niệm  Phân loại  Cú pháp  Ví dụ  Đặc điểm  Vấn đề 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:  Chương 4: Sự kế thừa và tính đa hình 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 class A{ }; class B: virtual public A{ }; class C: virtual public A{ }; class D:public B,public C{ }; 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.  Chương 4: Sự kế thừa và tính đa hình class person{ public: char name[20]; public: person(){strcpy(name, “chua biet");} void out(){cout<<"chao "<<name<<endl;} }; class teacher:public person{ public: teacher(){strcpy(name,“Phong");} void out(){cout<<"chao "<<name<<endl;} }; class student:public person{ public: student(){strcpy(name,“Teo");} void out(){cout<<"hi "<<name;} }; class assistant:public teacher,public student{ }; Cho biết kết quả của CT sau: void main(){ assistant a; cout<<a.name; a.out(); }  Cách khắc phục: void main(){ assistant a; cout<<a.student::name; a.teacher::out(); } Teo chao Phong  30  Cách 2: định nghĩa một hàm cụ thể trong lớp dẫn xuất.  Chương 4: Sự kế thừa và tính đa hình class person{ public: char name[20]; public: person(){strcpy(name, “chua biet");} void out(){cout<<"chao "<<name<<endl;} }; class teacher:public person{ public: teacher(){strcpy(name,“Phong");} void out(){cout<<"chao "<<name<<endl;} }; class student:public person{ public: student(){strcpy(name,“Teo");} void out(){cout<<"hi "<<name;} }; class assistant:public teacher,public student{ public: void out(){cout<<"hello "<<teacher::name; } }; Cho biết kết quả của CT sau: void main(){ assistant a; cout<<a.student::name; a.out(); } Teo hello Phong  31  Cách 3: sử dụng lớp cơ bản ảo trong kế thừa.  Chương 4: Sự kế thừa và tính đa hình class person{ public: char name[20]; public: person(){strcpy(name, “chua biet");} void out(){cout<<"chao "<<name<<endl;} }; class teacher: virtual public person{ public: teacher(){strcpy(name,“teacher");} void out(){cout<<"chao "<<name<<endl;} }; class student: virtual public person{ public: student(){strcpy(name,“student");} void out(){cout<<"hi "<<name;} }; class assistant:public teacher,public student{ public: assistant(){strcpy(name,“Phong");} void out(){cout<<"hello "<<name; } }; Cho biết kết quả của CT sau: void main(){ assistant a; cout<<a.name; a.out(); } Phong hello Phong  32  Cách 3: sử dụng lớp cơ bản ảo trong kế thừa.  Chương 4: Sự kế thừa và tính đa hình  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. 33 Khái niệm sự kế thừa Kế thừa đơn Đa kế thừa  Tính đa hình trong kế thừa  Chương 4: Sự kế thừa và tính đa hình 34 Tính đa hình trong kế thừa  Khái niệm  Ví dụ  Cú pháp  Khái niệm: Là cách thức truy cập các hàm khác nhau tùy thuộc vào đối tượng mà con trỏ đang trỏ tới khi ta thực thi cùng một lời gọi hàm. Ví dụ: • Đối với nhân viên trong cùng một công ty nhƣng cách tính lƣơng của nhân viên văn phòng khác cách tính lƣơng của nhân viên sản xuất. • 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.  Chương 4: Sự kế thừa và tính đa hình 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 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 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 thể tạo ra đối tƣợng.  Chương 4: Sự kế thừa và tính đa hình 36  Ví dụ 1: Cho biết kết quả của chƣơng trình sau.  Chương 4: Sự kế thừa và tính đa hình class hinh{ public: void ve(){cout<<"ve hinh "<<endl;} }; class hinhtron:public hinh{ public: void ve(){cout<<"hinh tron "<<endl;} }; class hinhchunhat:public hinh{ public: void ve(){cout<<"hinh cn "<<endl;} }; void main(){ hinhtron ht; hinhchunhat hcn; hinh *p; p=&ht; p->ve(); p=&hcn; p->ve(); } ve hinh ve hinh Con trỏ p đang trỏ vào đối tƣợng là hinhtron nhƣng kết quả lại xuất ra là “ve hinh” Con trỏ p đang trỏ vào đối tƣợng là hinhchunhat nhƣng kết quả vẫn xuất ra là “ve hinh” 37  Khắc phục: Để kết quả của chƣơng trình Ví dụ 1 thực thi đúng:  Chương 4: Sự kế thừa và tính đa hình class hinh{ public: virtual void ve(){cout<<"ve hinh "<<endl;} }; class hinhtron:public hinh{ public: void ve(){cout<<"hinh tron "<<endl;} }; class hinhchunhat:public hinh{ public: void ve(){cout<<"hinh cn "<<endl;} }; void main(){ hinhtron ht; hinhchunhat hcn; hinh *p; p=&ht; p->ve(); p=&hcn; p->ve(); } hinh tron hinh cn 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ì dùng hàm ve() của lớp hinhchunhat ĐH CÔNG NGHỆ THÔNG TIN 38 ĐH CÔNG NGHỆ THÔNG TIN 39 40 Câu 1: Cho biết kết quả của đoạn chương trình sau: class a{ public: void fun(){ cout<<"class a"; } }; class b: public a{ public: void fun(){ cout<<"class b"; } }; void main() { a *obj = new b(); obj->fun(); }  Bài tập trắc nghiệm A. class a class b B. class a C. Chƣơng trình báo lỗi D. class b 41 Câu 2: Cho biết kết quả của đoạn chương trình sau: class A{ public: virtual void In() { cout<<"A"; } }; class B: public A{ public: void In() { cout<<"B"; } }; class C: public B{