Có một vấn đề ở đây là static_cast rất dễ bị lạm dụng và sử dụng sai cách. Nếu bạn sử dụng nó một cách quá vô tư và hồn nhiên thì bạn có thể gặp rắc rối.
Hãy xem ví dụ này →
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 |
#include <iostream> #include <string> using namespace std; class Pet { protected: string Name; public: Pet(string n) { Name = n; } void Run(void) { cout << Name << ": I'm running" << endl; } }; class Dog : public Pet { public: Dog(string n) : Pet(n) {}; void MakeSound(void) { cout << Name << ": Woof! Woof!" << endl; } }; class Cat : public Pet { public: Cat(string n) : Pet(n) {}; void MakeSound(void) { cout << Name << ": Meow! Meow!" << endl; } }; int main(void) { Pet *a_pet1 = new Cat("Tom"); Pet *a_pet2 = new Dog("Spike"); a_pet2 -> Run(); static_cast<Cat *>(a_pet2) -> MakeSound(); a_pet1 -> Run(); static_cast<Dog *>(a_pet1) -> MakeSound(); return 0; } |
Ví dụ này cố gắng để đối xử với một con mèo như một con chó và ngược lại. Kết quả thật thảm khốc – chương trình sẽ tạo ra kết quả sau:
1 2 3 4 |
Spike: I'm running Spike: Meow! Meow! Tom: I'm running Tom: Woof! Woof! |
Con chó (tên là Spike thì kêu Meow! Meow!) còn con mèo (tên là Tom thì sủa Woof! Woof!). Đó là một cơn ác mộng.
Kết quả này là do trình biên dịch không thể kiểm tra nếu con trỏ (đã được chuyển đổi kiểu – ép kiểu) tương thích với đối tượng mà nó trỏ đến. Trình biên dịch hiểu rằng con trỏ là hợp lệ và nó tin rằng chúng ta hiểu rõ những gì chúng ta đang làm.
Việc kiểm tra tính hợp lệ của con trỏ một cách toàn diện chỉ có thể được thực hiện khi chương trình đang được chạy (runtime). Ngôn ngữ C++ có một toán tử ép kiểu thứ hai được thiết kế đặc biệt cho trường hợp này. Đó là dynamic_cast. Tên của nó đã nói lên rằng việc ép kiểu được thực hiện một cách linh động, phục thuộc vào hiện trạng của tất cả các đối tượng liên quan. Điều này có nghĩa là việc ép kiểu có thể thành công hoặc không thành công. OK, chúng ta sẽ sớm quay lại nói rõ hơn về vấn đề này.