Bài giảng Lập trình C/C++ - Chương 10: Lập trình hướng đối tượng (Chủ đề nâng cao) - Lê Thành Sách
Nội dung
n Đa thừa kế
n Đa thừa kế: thừa kế ảo
n Khởi động lớp cha trong thừa kế ảo
n Đa hình (polymorphism)
n Đa hình: hàm có tính abstract
n Tổng kết
Bạn đang xem 20 trang mẫu của tài liệu "Bài giảng Lập trình C/C++ - Chương 10: Lập trình hướng đối tượng (Chủ đề nâng cao) - Lê Thành Sách", để tải tài liệu gốc về máy hãy click vào nút Download ở trên
Tóm tắt nội dung tài liệu: Bài giảng Lập trình C/C++ - Chương 10: Lập trình hướng đối tượng (Chủ đề nâng cao) - Lê Thành Sách
Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 1 Chương 10 Lập trình hướng đối tượng -- chủ đề nâng cao -- Lê Thành Sách CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 2 Nội dung n Đa thừa kế n Đa thừa kế: thừa kế ảo n Khởi động lớp cha trong thừa kế ảo n Đa hình (polymorphism) n Đa hình: hàm có tính abstract n Tổng kết CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 3 Đa thừa kế n Là gì? n Là một lớp thừa kế các thành viên từ nhiều hơn một lớp, như ví dụ sau. Lớp ConsultantManager và PermanentManager, có đến 2 lớp cha. Trường hợp tổng quát: có thể có nhiều cha. CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 4 Đa thừa kế n Mô tả đa thừa kế ntn? n Liệt kê các lớp cha như ví dụ sau. n Sử dụng dấu phẩy để ngăn cách. CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 5 class Employee{ }; class TemporaryEmployee: public Employee{ }; class PermanentEmployee: public Employee{ }; class Consultant: public TemporaryEmployee{ }; class Manager: public Employee{ }; class ConsultantManager: public Consultant, public Manager{ }; class Director: public Manager{ }; class PermanentManager: public Manager, public PermanentEmployee{ }; Dùng dấu phẩy “,” để liệt kê các lớp cha CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 6 Đa thừa kế n Sơ đồ bộ nhớ của đối tượng n Giả sử có hệ thống lớp như hình vẽ, n Cũng giả sử code C++ được sinh ra như slide trước. n Xét dòng khai báo biến (tạo đối tượng) như sau: n Bộ nhớ của đối tượng “obj” được tổ chức ntn? PermanentManager obj; CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 7 Vùng nhớ của Employee Vùng nhớ của Manager Vùng nhớ của PermanentManager Vùng nhớ của Employee Vùng nhớ của PermanentEmployee Theo cách mô tả thừa kế như slide trước: Bên trong đối tượng kiểu “PermanentManager” có đến 2 đối tượng kiểu “Employee” hoàn toàn riêng biệt và khác nhau CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 8 Đa thừa kế: Minh hoạ (I) n Xét sơ đồ đa thừa kế như hình vẽ Hiện thực cho các lớp cho ở các slide kế tiếp CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 9 Đa thừa kế: Minh hoạ (I) class ClassA{ private: string derived_class_name; public: ClassA(string name): derived_class_name(name){ cout << "In ClassA(string name)" << endl; } void display(){ cout << "My Drived Class is " derived_class_name << endl; } }; Chứa tên của lớp con: ClassB hoặc ClassC Sẽ in ra tên lớp chứa trong biến derived_class_name è Sẽ là ClassB hoặc ClassC Khởi động biến CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 10 Đa thừa kế: Minh hoạ (I) class ClassB: public ClassA{ public: ClassB(string name): ClassA(name){ cout << "In ClassB(string name)" << endl; } }; ClassB thừa kế ClassA, với tính public Gọi hàm khởi tạo lớp ClassA CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 11 class ClassC: public ClassA{ public: ClassC(string name): ClassA(name){ cout << "In ClassC(string name)" << endl; } }; Đa thừa kế: Minh hoạ (I) ClassC thừa kế ClassA, với tính public Gọi hàm khởi tạo lớp ClassA CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 12 class ClassD: public ClassB, public ClassC{ public: ClassD(): ClassB("ClassB"), ClassC("ClassC") { } }; Đa thừa kế: Minh hoạ (I) ClassD thừa kế cả hai lớp ClassB và ClassC Gọi hàm khởi tạo của hai lớp cha: ClassB và ClassC CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 13 int main(){ ClassD obj; obj.ClassB::display(); obj.ClassC::display(); //obj.display(); return 0; }; Đa thừa kế: Minh hoạ (I) (1) obj: chứa bên trong đến 2 đối tượng kiểu ClassA (2): nếu gọi “display” như dòng này sẽ báo lỗi. Vì: có hai phiên bản của “display” cùng tồn tại, bộ biên dịch không biết phải dùng hàm nào. CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 14 int main(){ ClassD obj; obj.ClassB::display(); obj.ClassC::display(); //obj.display(); return 0; }; Đa thừa kế: Minh hoạ (I) In ra 4 dòng, vì sao? Sẽ báo lỗi nếu dùng! Kết quả chạy chương trình CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 15 Đa thừa kế: thừa kế ảo (virtual) n Thừa kế ảo là gì? n Như trường hợp ở slide trước: đối tượng của lớp cha (như ClassA ở trên) có thể được cấp phát lặp lại nhiều hơn 1 lần à không mong muốn n Đây là bài toán: “diamon problem” n Thừa kế ảo (virtual) giúp cho đối tượng của lớp cha (như ClassA ở trên) chỉ được cấp phát một lần. n Khai báo ntn? Như slide sau: CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 16 Đa thừa kế: thừa kế ảo (virtual) class ClassB: virtual public ClassA{ public: ClassB(string name): ClassA(name){ cout << "In ClassB(string name)" << endl; } }; class ClassC: virtual public ClassA{ public: ClassC(string name): ClassA(name){ cout << "In ClassC(string name)" << endl; } }; Từ khoá virtual CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 17 Đa thừa kế: thừa kế ảo (virtual) class ClassD: public ClassB, public ClassC{ public: ClassD(): ClassB("ClassB"), ClassC("ClassC") { } }; Không cần dùng virtual với ClassB và ClassC CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 18 Đa thừa kế: thừa kế ảo (virtual) n Vấn đề khởi động lớp cha: n (1) Gọi hàm khởi động cho lớp cha chung, như ClassA, phải từ lớp con chung, như lớp ClassD. n Các khởi động ở lớp trung gian, như ClassB và ClassC đều không có tác dụng. n (2) Nếu lớp con chung không gọi hàm khởi động của lớp cha chung thì hàm khởi tạo mặc nhiên (không thông số) của lớp cha chung sẽ được gọi. CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 19 Đa thừa kế: thừa kế ảo: Minh hoạ (I) n Nếu chỉ đơn giản thêm từ khoá “virtual” vào khai báo cho các lớp ClassB và ClassC như slide trước, sẽ có lỗi biên dịch n Lớp ClassA không có hàm khởi tạo mặc nhiên. n Lý do: n Lớp ClassD (con chung) không gọi hàm khởi tạo cho lớp ClassA è Hàm khởi tạo mặc nhiên của ClassA sẽ được gọi, nhưng nó không có – xem lớp ClassA. class ClassD: public ClassB, public ClassC{ public: ClassD(): ClassB("ClassB"), ClassC("ClassC") { } }; ClassD không khởi động cho ClassA CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 20 Đa thừa kế: thừa kế ảo: Minh hoạ (I) Nếu khởi động lớp ClassA tại lớp ClassD, và hàm main cho sau đây: class ClassD: public ClassB, public ClassC{ public: ClassD(): ClassA("From ClassD"), ClassB("ClassB"), ClassC("ClassC") { } }; CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 21 Đa thừa kế: thừa kế ảo: Minh hoạ (I) int main(){ ClassD obj; obj.ClassB::display(); obj.ClassC::display(); obj.display(); return 0; }; Kết quả chạy chương trình Vì sao? CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 22 Tính đa hình trong lập trình hướng đối tượng CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 23 Đa hình là gì? n Thuật ngữ n Đa hình = Polymorphism n Đa hình là một khả năng của OOP, hoạt động như sau: n (1) Một con trỏ kiểu lớp cha: n Như ví dụ: n n Tại lúc chương trình thực thi (run-time), có thể được gán địa chỉ của đối tượng kiểu lớp con, như ví dụ sau: n (2) Khi gọi hàm với con trỏ lớp cha (ptr ở trên) thì hàm trong lớp con được gọi, không phải hàm trong lớp cha. Base* ptr; ptr = new DerivedClass() ptr->foo(); CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 24 Đa hình là gì? n Tại sao gọi là đa hình n Hàm foo(), như slide trước, hoạt động như thế nào là tuỳ thuộc vào phiên bản nào (của lớp con nào) thật sự được gọi tại thời điểm thực thi n Cũng có nghĩa: chỉ mỗi một dòng lệnh n Cách hoạt động (hành xử) khác nhau n Nên được gọi là đa hình ptr->foo(); CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 25 Đa hình là gì? n Cho đến thời điểm này: n Lời gọi hàm: n Vẫn cứ gọi hàm foo() trong lớp cha, nghĩa là lớp BaseClass. n Xem ví dụ sau ptr->foo(); CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 26 Đa hình: Minh hoạ (I) class BaseClass{ public: void foo(){ cout << "BaseClass" << endl; } }; class DerivedClass: public BaseClass{ public: void foo(){ cout << "DerivedClass" << endl; } }; int main(){ BaseClass* ptr; ptr = new DerivedClass(); ptr->foo(); return 0; }; Kết quả chạy chương trình, in ra: BaseClass Vì sao? CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 27 Đa hình: Minh hoạ (I) class BaseClass{ public: void foo(){ cout << "BaseClass" << endl; } }; class DerivedClass: public BaseClass{ public: void foo(){ cout << "DerivedClass" << endl; } }; int main(){ BaseClass* ptr; ptr = new DerivedClass(); ptr->foo(); return 0; }; Lý do: mô tả hàm foo() như thế này, bộ biên dịch hiểu rằng hàm foo() trong phát biểu: ptr->foo() là của lớp BaseClass. Nghĩa là: xác định hàm được gọi tại thời điểm biên dịch, nên còn gọi là “ràng buộc sớm” (early binding) CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 28 Đa hình: Minh hoạ (I) Khai báo nào sẽ hỗ trợ tính đa hình? Trả lời: sử dụng từ khoá “virtual” như ví dụ sau. CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 29 Đa hình: Minh hoạ (I) class BaseClass{ public: virtual void foo(){ cout << "BaseClass" << endl; } }; class DerivedClass: public BaseClass{ public: void foo(){ cout << "DerivedClass" << endl; } }; int main(){ BaseClass* ptr; ptr = new DerivedClass(); ptr->foo(); return 0; }; Sử dụng từ khoá “virtual” Không cần lặp lại ở lớp con Khi chạy, in ra: DerivedClass CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 30 Đa hình: Minh hoạ (I) class BaseClass{ public: virtual void foo(){ cout << "BaseClass" << endl; } }; class DerivedClass: public BaseClass{ public: void foo() override{ cout << "DerivedClass" << endl; } }; int main(){ BaseClass* ptr; ptr = new DerivedClass(); ptr->foo(); return 0; }; Có thể dùng từ khoá override nếu muốn cho rõ ràng, từ khoá này nằm sau cùng, liền trước dấu { Khi chạy, in ra: DerivedClass CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 31 Đa hình: Minh hoạ (I) class BaseClass{ public: virtual void foo(){ cout << "BaseClass" << endl; } }; class DerivedClass: public BaseClass{ public: void foo() override{ cout << "DerivedClass" << endl; } }; int main(){ BaseClass* ptr; ptr = new DerivedClass(); ptr->foo(); return 0; }; Hàm foo() nào được gọi, trong lớp cha hay lớp con; trường hợp tổng quát thì lớp con nào. è Trong thời điểm thực thi mới biết. è Gọi là ràng buộc động (dynamic binding) CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 32 Đa hình: Bài tập ClassA virtual display(); ClassB display() override; ClassC ClassD display() override; ClassE Giả sử có hệ thống các lớp như hình, tất cả thừa kế đều có tính public. Con trỏ “ptr” được khai báo: ClassA* ptr; Phiên bản “display” của lớp nào được gọi trong các dòng sau đây? ptr = new ClassA(); ptr->display(); ptr = new ClassB(); ptr->display(); ptr = new ClassC(); ptr->display(); ptr = new ClassD(); ptr->display(); ptr = new ClassE(); ptr->display(); CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 33 Đa hình: Hàm có tính abstract n Hàm (phương thức) có tính abstract nghĩa là nó được khai báo rằng chưa có bản hiện thực nào gắn với nó, như ví dụ: class BaseClass{ public: virtual void foo() = 0; }; Hàm có tính abstract CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 34 Đa hình: Hàm có tính abstract n Hàm (phương thức) có tính abstract: n Các lớp con phải hiện thực hàm có tính này. n Lớp X nào có chứa hàm có tính này thì không thể tạo được đối tượng với kiểu lớp X được. n Tại sao phải dùng tính abstract: n Khi thiết kế các dự án lớn, có những lớp được thiết kế để chỉ quy định rằng lớp đó có hỗ trợ tính năng (hàm gì), còn hiện thực thì nằm ở lớp con thừa kế từ nó. n Với tính năng này cộng với đa thừa kế: C++ có thể hiện thực được khái niệm “interface” của Java. CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 35 Tổng kết n Đến lúc này, mọi tính năng quan trọng của phương pháp lập trình hướng đối tượng đã được giới thiệu và minh hoạ. n Các tính năng đã học trong phần này n Đa thừa kế. n Chú ý: n Khi đa thừa kế thì có khả năng tạo thành vòng n => dùng thừa kế ảo (từ khoá virtual) n Một khi đã dùng từ khoá virtual, thì n => Chú ý đến việc khởi động lớp cha chung từ lớp con chung. CuuDuongThanCong.com https://fb.com/tailieudientucntt Trường Đại Học Bách Khoa Tp.HCM Khoa Khoa học và Kỹ thuật Máy tính © 2017 Lập trình C/C++ 36 Tổng kết n Các tính năng đã học trong phần này n Tính đa hình n Đây là tính năng khá hay của OOP n Tính năng này có được là do công việc xác định hàm được gọi được thực hiện tại thời điểm chương trình thực thi (run-time), không phải tại thời điểm biên dịch (compile-time). n Cũng có nghĩa, n Những hàm không có tính “virtual” thì bộ biên dịch biết được phiên bản nào của hàm đó được dùng ngay tại thời điểm biên dịch. n Hàm có tính abstract, giúp cho quá trình thiết kế tách biệt giữa những quy định và hiện thực những quy định (khái niệm trừu tượng!) CuuDuongThanCong.com https://fb.com/tailieudientucntt
File đính kèm:
- bai_giang_lap_trinh_cc_chuong_10_lap_trinh_huong_doi_tuong_l.pdf