Đa hình là khả năng cho phép:
Cho phép các lớp được định nghĩa các phương thức trùng nhau: cùng tên, cùng số lượng và kiểu tham số, cùng kiểu trả về. Việc định nghĩa phương thức trùng nhau của các lớp kế thừa nhau còn được gọi là sự nạp chồng phương thức.
Khi gọi các phương thức trùng tên, dựa vào đối tượng đang gọi mà chương trình sẽ thực hiện phương thức của lớp tương ứng, và do đó, sẽ cho các kết quả khác nhau.
Ví dụ: Xây dựng lớp người, lớp sinh viên, lớp giáo viên cùng có phương thức Nhap() và phương thức Xuat().
Như vậy đa hình là khả năng cho phép viết chương trình để xử lý tổng quát các đối tượng của tất cả các lớp trong một phân cấp như các đối tượng của lớp cơ sở. Do vậy một thông điệp được gửi đi mà không cần biết đối tượng nhận thuộc lớp nào.
16 trang |
Chia sẻ: thuychi16 | Lượt xem: 800 | Lượt tải: 0
Bạn đang xem nội dung tài liệu Kĩ thuật lập trình - Bài 7: Đa hình, để tải tài liệu về máy bạn click vào nút DOWNLOAD ở trên
Bài 7: Đa hìnhGiới thiệu Đa hình là khả năng cho phép: Cho phép các lớp được định nghĩa các phương thức trùng nhau: cùng tên, cùng số lượng và kiểu tham số, cùng kiểu trả về. Việc định nghĩa phương thức trùng nhau của các lớp kế thừa nhau còn được gọi là sự nạp chồng phương thức. Khi gọi các phương thức trùng tên, dựa vào đối tượng đang gọi mà chương trình sẽ thực hiện phương thức của lớp tương ứng, và do đó, sẽ cho các kết quả khác nhau. Ví dụ: Xây dựng lớp người, lớp sinh viên, lớp giáo viên cùng có phương thức Nhap() và phương thức Xuat(). Như vậy đa hình là khả năng cho phép viết chương trình để xử lý tổng quát các đối tượng của tất cả các lớp trong một phân cấp như các đối tượng của lớp cơ sở. Do vậy một thông điệp được gửi đi mà không cần biết đối tượng nhận thuộc lớp nào. Các phương thức ảo Xét ví dụ3 lớp A,B,C đều có phương thức xuat()A *p, *q, *r; // p, q, r là con trỏ kiểu AA a; // a là đối tượng kiểu AB b; // b là đối tượng kiểu BC c; // c là đối tượng kiểu c//-----------------p = &a ;q = &b ;r = &c ;//------------p->xuat();q->xuat();r->xuat();Cả ba câu lệnh đều gọi đến A::xuat()Giải pháp: Xây dựng xuat() là phương thức ảoCác phương thức ảo Giả sử A là lớp cơ sở. B,C,D là lớp dẫn xuất. Giả sử các lớp này đều có phương thức xuat(). Để định nghĩa các phương thức này là ảo có 2 cách: Hoặc thêm từ khoá virtual vào dòng tiêu đề của phương thức bên trong định nghĩa lớp cơ sở A. Hoặc thêm từ khoá virtual vào dòng tiêu đề bên trong định nghĩa của tất cả các lớp A, B, C và DCác phương thức ảo Khi xây dựng một cấu trúc cây phân cấp, người lập trình chuẩn bị các hành vi chung của lớp đó. Hành vi giao tiếp chung sẽ được dùng để thể hiện cùng hành vi, nhưng có các hành động khác nhau đó là phương thức ảo. Ví dụ: Đọc thêm ví dụ trang 122 - giáo trình LTHĐTCác phương thức ảoclass Base{ public: virtual void Display() { coutDisplay(); //Con trỏ B chỉ đến phương thức Display()//(của lớp Base hoặc lớp Derived) //tùy vào lúc chạy chương trình. }int main(){ Base *B=new Base; Derived *D=new Derived; B->Display(); //Base::Display() D->Display(); //Derived::Display() Show(B); //Base::Display() Show(D); //Derived::Display() return 0;}Các phương thức ảo Kết quả khi chạy chương trình: Giải thích: Nếu không có khai báo virtual cho phương thức Base::Display() khi đó lệnh Show(D) trong hàm main() sẽ gọi đến Base::Display() (vì đối tượng của lớp dẫn xuất cũng là đối tượng của lớp cơ sở).Nhờ khai báo virtual cho phương thức Base::Display() nên sẽ không thực hiện gọi phương thức Base::Display() một cách cứng nhắc trong hàm Show() mà tùy thuộc vào kiểu của tham số vào lúc chạy chương trình. ( khi truyền tham số B thì Base::Display() được gọi, khi truyền D thì Derived::Display() được gọi). Các phương thức ảo Giải thích cơ chế: Khi nhận thấy có khai báo virtual trong lớp cơ sở, trình biên dịch sẽ thêm vào mỗi đối tượng của lớp cơ sở và lớp dẫn xuất của nó một con trỏ chỉ đến bảng phương thức ảo (virtual function table), con trỏ có tên vptr. Bảng phương thức ảo là nơi chứa các con trỏ chỉ đến đoạn chương trình đã biên dịch ứng với các phương thức ảo. Mỗi lớp có một bảng phương thức ảo. Trình biên dịch chỉ lập bảng phương thức ảo khi bắt đầu có việc tạo đối tượng của lớp. Đến khi chương trình chạy, phương thức ảo của đối tượng mới được nối kết và thi hành thông qua con trỏ vptrCác phương thức ảo Giải thích cơ chế: Xét ví dụ trên: Hàm Show(D). Đối tượng D thuộc lớp Derived tuy bị chuyển đổi kiểu thành một đối tượng thuộc lớp Base nhưng nó không hoàn toàn giống một đối tượng của Base chính cống như B. Con trỏ vptr trong B chỉ đến vị trí trên bảng phương thức ảo ứng với phương thức Base::Display(), trong khi con trỏ vptr trong D vẫn còn chỉ đến phương thức Derived::Display() cho dù D bị chuyển kiểu thành Base. Do vậy lệnh: Show(D);gọi đến phương thức Derived::Display()Các phương thức ảo Đặc trưng của phương thức ảo. Phương thức ảo không thể là các phương thức tĩnh. Không cần thiết phải ghi rõ từ khóa virtual trước định nghĩa một phương thức ảo trong lớp dẫn xuất. Khi một phương thức được định nghĩa là ảo, từ lớp cơ sở đến lớp dẫn xuất đều phải định nghĩa thống nhất về tên, kiểu trả về và danh sách các tham số. Nếu ta sơ xuất định nghĩa phương thức khác đi thì trình biên dịch sẽ hiểu đó là phương thức khác.Lớp cơ sở trừu tượng Trong quá trình thiết kế hướng đối tượng, để tạo hệ thống phả hệ mang tính kế thừa cao người lập trình phải đoán trước sự phát triển của cấu trúc từ đó lựa chọn những thành viên phù hợp cho lớp trên cùng. Để tránh tình trạng xây dựng các đối tượng lãng phí bộ nhớ, C++ cho phép thiết kế các lớp có các phương thức ảo không làm gì cả, và cũng không thể tạo ra đối tượng của lớp đó. Những lớp như vậy gọi là lớp trừu tượng.Lớp cơ sở trừu tượng Là lớp chỉ dùng làm cơ sở cho các lớp khác. Không được khởi tạo một đối tượng thuộc lớp trừu tượng. Các phương thức của lớp trừu tượng là các phương thức ảo thuần túy. virtual void tên_phương_thức() = 0 ; Bất kỳ lớp nào dẫn xuất từ một lớp cớ sở trừu tượng phải định nghĩa lại tất cả các phương thức thuần ảo mà nó thừa hưởng.Lớp cơ sở trừu tượng Trong cây phân cấp trên không phải lớp nào cũng cần hàm print(), nhưng nó được tạo ra ở khắp nơi để tạo bộ mặt chung cho mọi lớp của cây phân cấp. A::Print() thường là phương thức ảo để có được tính đa hình.Lớp cơ sở trừu tượng Khi đó với hàmvoid Show(A* a){ a->Print();}ta có thể truyền đối tượng đủ kiểu cho nó (A, B, C, D hoặc E) mà vẫn gọi đến đúng phuơng thức Print() phù hợp, dù kiểu của đối tượng lúc biên dịch vẫn còn chưa biết. Để tránh trình trạng vô tình tạo ra đối tượng thuộc lớp A, ta thường xây dựng lớp trừu tượng với các phương thức thuần ảo (pure virtual function) như sau:class A{ public: virtual void Print() = 0;}; Xem ví dụ trang 126- giáo trình LTHDTLớp cơ sở trừu tượngChú ý: Chúng ta không thể tạo ra một đối tượng của lớp trừu tượng, nhưng hoàn toàn có thể tạo ra một con trỏ trỏ đến lớp này (vì con trỏ không phải là đối tượng thuộc lớp) hoặc là một tham chiếu. Nếu trong lớp kế thừa từ lớp trừu tượng chúng ta không định nghĩa phương thức thuần ảo, do tính kế thừa nó sẽ bao hàm phương thức thuần ảo của lớp cơ sở, nên lớp dẫn xuất này sẽ trở thành lớp trừu tượng. Theo định nghĩa lớp trừu tượng, nếu trong lớp dẫn xuất (từ lớp cơ sở trừu tượng) chúng ta định nghĩa thêm một phương thức thuần ảo khác, lớp này cũng sẽ trở thành lớp trừu tượng.Các thành viên ảo của một lớp Các toán tử ảo: Các toán tử bản chất cũng là các phương thức nên ta có thể định nghĩa các toán tử là ảo, chú ý kiểu của toán hạng phải sử dụng kiểu của lớp cơ sở gốc có toán tử ảo. Ví dụclass A{ virtual A& operator + (A& T); virtual A& operator = (A& T); }Class B: public A{ virtual A& operator + (A& T); virtual A& operator = (A& T); }