Tôi thực sự không thể hiểu lý do tại sao trình biên dịch không gọi hàm của cá thể lớp thuộc về?
Đó là những gì trình biên dịch thực hiện - đảm bảo rằng chương trình của bạn gọi hàm của lớp mà cá thể đó thuộc về. Từ khóa ở đây là dụ: kiến thức về lớp của cá thể không có sẵn tại thời gian biên dịch.
Hãy xem xét ví dụ đơn giản này:
struct Dude {
virtual void howdy() = 0;
};
struct Bob : public Dude {
virtual void howdy() { cout << "Hi, Bob!" << endl; }
};
struct Moe : public Dude {
virtual void howdy() { cout << "Hi, Moe!" << endl; }
};
// Note the pass by reference below: passing by reference or by pointer,
// but not by value, is important for achieving polymorphic behavior.
void say_hi(Dude& dude) {
dude.howdy(); // <<== Here is the tricky line
}
int main(int argc, char* argv[]) {
Bob b;
Moe m;
Dude *d = rand() & 1 ? (Dude*)&b : &m;
say_hi(*d);
}
Lưu ý dòng khó khăn: trình biên dịch có thể hiện, nhưng nó không biết lớp. Trên thực tế, nó là loại biết lớp, nhưng không phải là một trong những cụ thể nhất. Kiến thức của trình biên dịch có lúc biên dịch là đủ để biết rằng có một hàm gọi là howdy
, nhưng không đủ để quyết định cái nào trong số một số khả năng sẽ là một trong những hàm gọi trong thời gian chạy.
Đây là nơi vtables đến để giải cứu: trình biên dịch biết rằng các lớp con của Dude
sẽ có một con trỏ đến hàm howdy
được nhúng ở đâu đó vào vtable của chúng. Đó là tất cả những gì họ cần biết khi biên dịch! Họ chèn một cuộc gọi ảo tìm kiếm con trỏ hàm trong thời gian chạy, đạt được hành vi mà bạn mong đợi (từ ưa thích cho loại hành vi đó là "đa hình"). Dưới đây là một số demo of this program chạy trên ideone.
Lý do sử dụng vtable là khi một lớp dẫn xuất thực hiện lại một hàm ảo, trong quá trình khởi tạo của lớp dẫn xuất, vtable được cập nhật để trỏ đến việc thực thi có nguồn gốc thay cho cơ sở. – Borgleader
Hai từ: * công văn động *. Mặc dù có những giải thích tốt dưới đây, bạn cần ít nhất là nghe thuật ngữ chính xác. –