Hãy cùng phân tích source code bên dưới (đây là source code của chương trình ở trang trước nhưng đã được thay đổi) →
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 |
#include <iostream> #include <string> using namespace std; class Pet { protected: string name; public: Pet(string name) : name(name) {} virtual void MakeSound(void) { cout << name << " is silent" << endl; } }; class Dog : public Pet { public: Dog(string name) : Pet(name) {} void MakeSound(void) { cout << name << " says: Woof!" << endl; } }; class GermanShepherd : public Dog { public: GermanShepherd(string name) : Dog(name) {} void MakeSound(void) { cout << name << " says: Wuff!" << endl; } void Laufen(void) { cout << name << " runs (gs)!" << endl; } }; class MastinEspanol : public Dog { public: MastinEspanol(string name) : Dog(name) {} void MakeSound(void) { cout << name << " says: Guau!" << endl; } void Ejecutar(void) { cout << name << " runs (mes)!" << endl; } }; void PlayWithPet(Pet *pet) { GermanShepherd *gs; MastinEspanol *mes; pet -> MakeSound(); if(gs = dynamic_cast<GermanShepherd *>(pet)) { gs -> Laufen(); } if(mes = dynamic_cast<MastinEspanol *>(pet)) { mes -> Ejecutar(); } } int main(void) { Pet *pet = new Pet("creature"); Dog *dog = new Dog("Dog"); GermanShepherd *gs = new GermanShepherd("Hund"); MastinEspanol *mes = new MastinEspanol("Perro"); PlayWithPet(pet); PlayWithPet(dog); PlayWithPet(gs); PlayWithPet(mes); return 0; } |
Hãy nói về những thay đổi đã được thực hiện.
- Thứ nhất, hàm MakeSound bên trong lớp Pet (cấp cao nhất trong phân cấp lớp) bây giờ được chuyển thành hàm ảo.
- Thứ hai, chúng tôi đã làm cho class tree trở nên sâu hơn. Chúng tôi đã thêm hai cấp độ bổ sung vào cây. Mức thấp nhất bao gồm hai nhánh có chứa các loài rất cụ thể: GermanShepherd và MastinEspanol.
Hàm PlayWithPet nhận một con trỏ kiểu Pet * (tức là con trỏ tới đối tượng ở cấp cao nhất). Hàm này làm hai điều quan trọng:
- Nó gọi hàm MakeSound (hàm này là hàm ảo).
- Nó cố gắng để nhận ra bản chất của con trỏ đã nhận và buộc đối tượng được trỏ đến (bởi con trỏ) hành xử theo đúng bản chất nguồn gốc của nó – đây là thời điểm mà toán tử dynamic_cast trở nên không thể thiếu.
Bây giờ, chúng ta sẽ có các quy tắc sau:
Nếu toán tử dynamic_cast được sử dụng theo cách sau:
dynamic_cast <pointer_type> (pointer_to_object)
và việc chuyển đổi kiểu của pointer_to_object thành pointer_type là có thể, thì kết quả của việc chuyển đổi sẽ là một con trỏ mới hoàn toàn có thể sử dụng. Nếu không, kết quả của việc chuyển đổi sẽ bằng NULL.
Cơ chế này thuận tiện cho phép chúng ta nhận ra bản chất của con trỏ. Hãy xem câu lệnh sau:
1 2 3 |
if(gs = dynamic_cast<GermanShepherd *>(pet)) { gs -> Laufen(); } |
Nếu con trỏ pet xác định một đối tượng của lớp GermanShepherd hoặc (đừng quên!) bất kỳ lớp con nào của GermanShepherd, chúng ta có thể sử dụng con trỏ đã được chuyển đổi lưu trữ trong biến gs. Nếu không chúng ta sẽ méo làm gì cả.
Nếu biên dịch và chạy chương trình bên trên, chúng ta sẽ thấy nó cho ra màn hinh như sau:
1 2 3 4 5 6 |
creature is silent Dog says: Woof! Hund says: Wuff! Hund runs (gs)! Perro says: Guau! Perro runs (mes)! |