Chương 6 Tương ứng bội và phương thức ảo Phương thức ảo và tương ứng bội

8 264 0
Chương 6 Tương ứng bội và phương thức ảo Phương thức ảo và tương ứng bội

Đang tải... (xem toàn văn)

Thông tin tài liệu

Phương thức ảo tương ứng bội 3.1. Cách định nghĩa phương thức ảo Giả sử A là lớp cơ sở, các lớp B, C, D dẫn xuất (trực tiếp hoặc dán tiếp) từ A. Giả sử trong 4 lớp trên đều có các phương thức trùng dòng tiêu đề (trùng kiểu, trùng tên, trùng các đối). Để định nghĩa các phương thức này là các phương thức ảo, ta chỉ cần: + 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 D. Ví dụ: Cách 1: class A { . virtual void hien_thi() { cout << “\n Đây là lớp A” ; }; } ; class B : public A { . void hien_thi() { cout << “\n Đây là lớp B” ; }; } ; class C : public B { . void hien_thi() { cout << “\n Đây là lớp C” ; }; } ; class D : public A { . 329 330 void hien_thi() { cout << “\n Đây là lớp D” ; }; } ; Cách 2: class A { . virtual void hien_thi() { cout << “\n Đây là lớp A” ; }; } ; class B : public A { . virtual void hien_thi() { cout << “\n Đây là lớp B” ; }; } ; class C : public B { . virtual void hien_thi() { cout << “\n Đây là lớp C” ; }; } ; class D : public A { . virtual void hien_thi() { cout << “\n Đây là lớp D” ; }; } ; Chú ý: Từ khoá virtual không được đặt bên ngoài định nghĩa lớp. Ví dụ nếu viết như sau là sai (CTBD sẽ báo lỗi). class A 331 332 { . virtual void hien_thi() ; } ; virtual void hien_thi() // Sai { cout << “\n Đây là lớp A” ; }; Cần sửa lại như sau: class A { . virtual void hien_thi() ; } ; void hien_thi() // Đúng { cout << “\n Đây là lớp A” ; }; 3.2. Quy tắc gọi phương thức ảo Để có sự so sánh với phương thức tĩnh, ta nhắc lại quy tắc gọi phương thức tĩnh nêu trong § 1. 3.2.1. Quy tắc gọi phương thức tĩnh Lời gọi tới phương thức tĩnh bao giờ cũng xác định rõ phương thức nào (trong số các phương thức trùng tên của các lớp có quan hệ thừa kế) được gọi: 1. Nếu lời gọi xuất phát từ một đối tượng của lớp nào, thì phương thức của lớp đó sẽ được gọi. 2. Nếu lời gọi xuất phát từ một con trỏ kiểu lớp nào, thì phương thức của lớp đó sẽ được gọi bất kể con trỏ chứa địa chỉ của đối tượng nào. 3.2.2. Quy tắc gọi phương thức ảo Phương thức ảo chỉ khác phương thức tĩnh khi được gọi từ một con trỏ (trường hợp 2 nêu trong mục 3.2.1). Lời gọi tới phương thức ảo từ một con trỏ chưa cho biết rõ phương thức nào (trong số các phương thức ảo trùng tên của các lớp có quan hệ thừa kế) sẽ được gọi. Điều này phụ thuộc vào đối tượng cụ thể mà con trỏ đang trỏ tới: Con trỏ đang trỏ tới đối tượng của lớp nào thì phương thức của lớp đó sẽ được gọi. Ví dụ A, B, C D là các lớp đã định nghĩa trong 3.1. Ta khai báo một con trỏ kiểu A 4 đối tượng: A *p ; // p là con trỏ kiểu A A a ; // a là biến đối tượng kiểu A B b ; // b là biến đối tượng kiểu B C c ; // c là biến đối tượng kiểu C D d ; // d là biến đối tượng kiểu D Xét lời gọi tới các phương thức ảo hien_thi sau: p = &a; // p trỏ tới đối tượng a của lớp A p->hien_thi() ; // Gọi tới A::hien_thi() p = &b; // p trỏ tới đối tượng b của lớp B p->hien_thi() ; // Gọi tới B::hien_thi() p = &c; // p trỏ tới đối tượng c của lớp C p->hien_thi() ; // Gọi tới C::hien_thi() p = &d; // p trỏ tới đối tượng d của lớp D p->hien_thi() ; // Gọi tới D::hien_thi() 3.3. Tương ứng bội Chúng ta nhận thấy cùng một câu lệnh p->hien_thi(); tương ứng với nhiều phương thức khác nhau. Đây chính là tương ứng bội. Khả năng này rõ ràng cho phép xử lý nhiều đối tượng khác nhau, nhiều công việc, thậm chí nhiều thuật toán khác nhau theo cùng một cách thức, cùng một lược đồ. Điều này sẽ được minh hoạ trong các mục tiếp theo. 3.4. Liên kết động Có thể so sánh sự khác nhau giữ phương thức tĩnh phương thức ảo trên khía cạnh liên kết một lời gọi với một phương thức. Trở lại ví dụ trong 3.2: A *p ; // p là con trỏ kiểu A A a ; // a là biến đối tượng kiểu A B b ; // b là biến đối tượng kiểu B C c ; // c là biến đối tượng kiểu C D d ; // d là biến đối tượng kiểu D Nếu hien_thi() là các phương thức tĩnh, thì dù p chứa địa chỉ của các đối tượng a, b, c hay d, thì lời gọi: p->hien_thi() ; luôn luôn gọi tới phương thức A::hien_thi() Như vậy một lời gọi (xuất phát từ con trỏ) tới phương thức tĩnh luôn luôn liên kết với một phương thức cố định sự liên kết này xác định trong quá trình biên dịch chương trình. Cũng với lời gọi: p->hien_thi() ; như trên, nhưng nếu hien_thi() là các phương thức ảo, thì lời gọi này không liên kết cứng với một phương thức cụ thể nào. Phương thức mà nó liên kết (gọi tới) còn chưa xác định trong giai đoạn dịch chương trình. Lời gọi này sẽ: + liên kết với A::hien_thi() , nếu p chứa địa chỉ đối tượng lớp A 333 334 + liên kết với B::hien_thi() , nếu p chứa địa chỉ đối tượng lớp B + liên kết với C::hien_thi() , nếu p chứa địa chỉ đối tượng lớp C + liên kết với D::hien_thi() , nếu p chứa địa chỉ đối tượng lớp D Như vậy một lời gọi (xuất phát từ con trỏ) tới phương thức ảo không liên kết với một phương thức cố định, mà tuỳ thuộc vào nội dung con trỏ. Đó là sự liên kết động phương thức được liên kết (được gọi) thay đổi mỗi khi có sự thay đổi nội dung con trỏ trong quá trình chạy chương trình. 3.5. Quy tắc gán địa chỉ đối tượng cho con trỏ lớp cơ sở + Như đã nói trong § 1, C++ cho phép gán địa chỉ đối tượng của một lớp dẫn xuất cho con trỏ của lớp cơ sở. Như vậy các phép gán sau (xem 3.2) là đúng: A *p ; // p là con trỏ kiểu A A a ; // a là biến đối tượng kiểu A B b ; // b là biến đối tượng kiểu B C c ; // c là biến đối tượng kiểu C D d ; // d là biến đối tượng kiểu D p = &a; // p a cùng lớp A p = &b; // p là con trỏ lớp cơ sở, b là đối tượng lớp dẫn xuất p = &c; // p là con trỏ lớp cơ sở, c là đối tượng lớp dẫn xuất p = &d; // p là con trỏ lớp cơ sở, d là đối tượng lớp dẫn xuất + Tuy nhiên cần chú ý là: Không cho phép gán địa chỉ đối tượng của lớp cở sở cho con trỏ của lớp dẫn xuất. Như vậy ví dụ sau là sai: B *q ; A a ; q = &a; Sai vì: Gán địa chỉ đối tượng của lớp cơ sở A cho con trỏ của lớp dẫn xuất B 3.6. Ví dụ Ta sửa chương trình trong § 1 bằng cách định nghĩa các phương thức xuat() là ảo. Khi đó bốn câu lệnh: hien(&a); hien(&b); hien(&c); hien(&d); trong hàm main (của chương trình dưới đây) sẽ lần lượt gọi tới 4 phương thức khác nhau: A::xuat() B::xuat() C::xuat() D::xuat() //CT6-01B // Phuong thuc ảo tương ứng bội #include <conio.h> 335 336 #include <stdio.h> #include <iostream.h> #include <ctype.h> class A { private: int n; public: A() { n=0; } A(int n1) { n=n1; } virtual void xuat() { cout << "\nLop A: "<< n; } int getN() { return n; } }; class B:public A { public: B():A() { } B(int n1):A(n1) { } void xuat() { cout << "\nLop B: "<<getN(); } }; class C:public A { public: C():A() { } C(int n1):A(n1) { } void xuat() { cout << "\nLop C: "<<getN(); } }; class D:public C { public: D():C() { } D(int n1):C(n1) { } void xuat() { cout << "\nLop D: "<<getN(); } }; void hien(A *p) { p->xuat(); } void main() { A a(1); B b(2); C c(3); D d(4); clrscr(); hien(&a); hien(&b); 337 338 hien(&c); hien(&d); getch(); } 3.5. Sự thừa kế của các phương thức ảo Cũng giống như các phương thức thông thường khác, phương thức ảo cũng có tính thừa kế. Chẳng hạn trong chương trình trên (mục 3.4) ta bỏ đi phương thức xuat() của lớp D, thì câu lệnh: hien(&d) ; (câu lệnh gần cuối trong hàm main) sẽ gọi tới C::xuat() , phương thức này được kế thừa trong lớp D (vì D dẫn xuất từ C). . gọi phương thức ảo Phương thức ảo chỉ khác phương thức tĩnh khi được gọi từ một con trỏ (trường hợp 2 nêu trong mục 3.2.1). Lời gọi tới phương thức ảo từ. Phương thức ảo và tương ứng bội 3.1. Cách định nghĩa phương thức ảo Giả sử A là lớp cơ sở, các lớp B, C, D dẫn

Ngày đăng: 24/10/2013, 15:20

Từ khóa liên quan

Tài liệu cùng người dùng

  • Đang cập nhật ...

Tài liệu liên quan