CHƢƠNG 4 XỬ LÝ ĐA HÌNH
2. CƠ CHẾ XỬ LÝ ĐA HÌNH
2.1. Phƣơng thức ảo
Để đƣa một phƣơng thức trở thành một phƣơng thức ảo thì chỉ cần đặt từ khĩa
virtual trƣớc phƣơng thức.
Vậy sử dụng phƣơng thức ảo chúng ta sẽ thu đƣợc kết quả gì? Chúng ta cùng xem xét ví dụ sau:
Tài liệu giảng dạy Lập Trình Hƣớng Đối Tƣợng 88 Kết quả thu đƣợc là: A-B-C
Bây giờ chúng ta viết lại hàm main ở ví dụ trên nhƣ sau:
void main() { A a;
B b; C c; A*p;
p=&a; // p tro vao a
p->print(); //xuat A
p= &b; // p tro vao b
Tài liệu giảng dạy Lập Trình Hƣớng Đối Tƣợng 89 Kết quả vẫn cho là A-B-C. Nhƣ vậy trong ví dụ này phƣơng thức print() là phƣơng thức ảo thì chúng ta đã giải đã đáp ứng đƣợc mong muốn tức là con trỏ đang trỏ đến đối tƣợng nào thì nĩ phải gọi đến phƣơng thức của lớp đĩ ngay cả khi con trỏ đĩ là con trỏ của lớp khác.
Vậy tính đa hình đƣợc thể hiện nhƣ thế nào trong ví dụ này?
Chúng ta thấy cùng là một hành vi pprint() nhƣng tùy vào đối tƣợng hiện tại mà p đang trỏ tới mà hành vi đĩ đƣợc thể hiện khác nhau. Nếu hiện tại p đang trỏ vào đối tƣợng a thì gọi phƣơng thức của đối tƣợng a tức xuất ra “A”, cịn nếu p đang trỏ vào b thì sẽ gọi phƣơng thức print() của đối tƣợng b tức xuất ra “B” và nếu trỏ vào c thì xuất ra “C”. Khả năng này gọi là tính đa hình hay đa xạ hay tƣơng ứng bội trong C++.
Nhƣ vậy phƣơng thức ảo là gì? - Là một phƣơng thức của lớp - Khai báo: virtual <khai báo hàm>; - Mang tính ảo
- Nhờ phƣơng thức ảo mà chuyển lời gọi hàm cho đúng đối tƣợng con trỏ đang trỏ đến. Điều này đƣợc gọi liên kết động hay nĩi cách khác đây là cơ chế cho phép gởi một loại thơng điệp tới nhiều đối tƣợng khác nhau mà mỗi đối tƣợng lại cĩ cách xử lý riêng theo ngữ cảnh tƣơng ứng của chúng.
2.2. Các lƣu ý khi sử dụng phƣơng thức ảo
- Phƣơng thức ảo chỉ cĩ ý nghĩa khi gọi thơng qua con trỏ
- Muốn một hàm trở thành phƣơng thức ảo cĩ hai cách: khai báo với từ khĩa virtual hoặc hàm tƣơng ứng ở lớp cơ sở đã là phƣơng thức ảo - Phƣơng thức ảo chỉ hoạt động nếu các hàm ở lớp cơ sở và lớp con cĩ
nghi thức giao tiếp giống hệt nhau.
- Nếu ở lớp con định nghĩa lại phƣơng thức ảo (viết khác tên phƣơng thức) thì sẽ gọi phƣơng thức ở lớp cơ sở (gần nhất cĩ định nghĩa) 2.3. Các thành viên ảo của một lớp
Cĩ constructor và destructor ảo hay khơng?
p=&c;
p->print(); // xuat C
Tài liệu giảng dạy Lập Trình Hƣớng Đối Tƣợng 90 Khi một đối tƣợng thuộc lớp cĩ phƣơng thức ảo, để thực hiện cơ chế kết nối động, trình biên dịch sẽ tạo thêm một con trỏ vptr nhƣ một thành viên của lớp, con trỏ này cĩ nhiệm vụ quản lý địa chỉ của phƣơng thức ảo. Một lớp chỉ cĩ một bảng phƣơng thức ảo, trong khi đĩ cĩ thể cĩ nhiều đối tƣợng thuộc lớp, nên khi một đối tƣợng khác thuộc cùng lớp tạo ra thì con trỏ vptr đã tồn tại. Chính vì vậy bảng phƣơng thức ảo phải đƣợc tạo ra trƣớc khi gọi thực hiện constructor, nên constructor khơng thể là phƣơng thức ảo.
Ngƣợc lại do một lớp chỉ cĩ một bảng phƣơng thức ảo nên khi một đối tƣợng thuộc lớp bị hủy bỏ, bảng phƣơng thức ảo vẫn cịn đĩ, và con trỏ vptr vẫn cịn đĩ. Hơn nữa, destructor đƣợc gọi thực hiện trƣớc khi vùng nhớ dành cho đối tƣợng bị thu hồi, do đĩ destructor cĩ thể là phƣơng thức ảo. Tuy nhiên, constructor của một lớp cĩ thể gọi phƣơng thức ảo khác. Điều này hồn tồn khơng cĩ gì mâu thuẫn với cơ chế kết nối động. Ví dụ: 1: // Destructor ảo 2: #include<iostream.h> 3: 4: class Base 5: { 6: public: 7: virtual ~Base() 8: { 9: cout<<”~Base”<<endl; 10: } 11: };
Tài liệu giảng dạy Lập Trình Hƣớng Đối Tƣợng 91 Kết quả:
Nếu destructor khơng là phƣơng thức ảo thì khi giải phĩng đối tƣợng B chỉ cĩ destructor của lớp cơ sở đƣợc gọi mà thơi nhƣng khi destructor là phƣơng thức ảo thì khi giải phĩng đối tƣợng B (ở dịng 25) destructor của lớp dẫn xuất đƣợc gọi thực hiện rồi đến destructor của lớp cơ sở.
Phƣơng thức hủy bỏ ảo:
Nếu phƣơng thức hủy khơng phải là phƣơng thức ảo thì chỉ cĩ phƣơng thức hủy của lớp cơ sở đƣợc gọi, cịn phƣơng thức hủy của lớp dẫn xuất khơng đƣợc gọi. Để đảm bảo việc dọn dẹp là đầy đủ, ta dùng phƣơng thức hủy bỏ ảo.
Ví dụ xét quan hệ sau:
12:
13: class Derived:public Base 14:{ 15: public: 16: virtual ~Derived() 17: { 18: cout<<”~Derived”<<endl; 19: } 20: }; 21: int main() 22: { 23: Base *B; 24: B=new Derived; 25: detete B; 26: return 0; 27: } ~Derived ~Base
Tài liệu giảng dạy Lập Trình Hƣớng Đối Tƣợng 92