Hoặc, có bất kỳ ảnh hưởng tiêu cực nào khác được biết đến của việc sử dụng __declspec (novtable) không? Tôi dường như không thể tìm thấy tài liệu tham khảo cho bất kỳ vấn đề nào.Có sử dụng __declspec (novtable) trên các lớp cơ sở trừu tượng ảnh hưởng đến RTTI trong bất kỳ cách nào?
Trả lời
MSCV sử dụng one vptr per object and one vtbl per class
để triển khai cơ chế OO như RTTI và các chức năng ảo.
Vì vậy, các hàm RTTI và ảo sẽ hoạt động tốt nếu và chỉ khi vptr được đặt chính xác.
struct __declspec(novtable) B {
virtual void f() = 0;
};
struct D1 : B {
D1() {
} // after the construction of D1, vptr will be set to vtbl of D1.
};
D1 d1; // after d has been fully constructed, vptr is correct.
B& b = d1; // so virtual functions and RTTI will work.
b.f(); // calls D1::f();
assert(dynamic_cast<D1*>(&b));
assert(typeid(b) == typeid(D1));
B phải là lớp trừu tượng khi sử dụng __declspec(novtable)
.
Sẽ không có trường hợp nào của B ngoại trừ trong hàm tạo của D1.
Và __declspec (novtable) không có tác động tiêu cực trong hầu hết trường hợp.
Nhưng trong khi xây dựng lớp dẫn xuất __declspec(novtable)
sẽ làm cho nó khác với ngữ nghĩa ISO C++.
struct D2 : B {
D2() { // when enter the constructor of D2 \
// the vtpr must be set to vptr of B \
// if B didn't use __declspec(novtable).
// virtual functions and RTTI will also work.
this->f(); // should calls B::f();
assert(typeid(*this) == typeid(B));
assert(!dynamic_cast<D2*>(this));
assert(dynamic_cast<B*>(this));
// but __declspec(novtable) will stop the compiler \
// from generating code to initialize the vptr.
// so the code above will crash because of uninitialized vptr.
}
};
Lưu ý: virtual f() = 0
; làm cho f là pure virtual function
và B trở thành lớp trừu tượng.
definition
của chức năng ảo tinh khiết could
(không must
) bị thiếu.
C++ cho phép gọi hàm ảo trong hàm tạo mà chúng tôi không đề xuất.
Cập nhật: Lỗi trong D2: hàm vptr trong hàm tạo có nguồn gốc.
struct D3 : B { // ISO C++ semantic
D3() { // vptr must be set to vtbl of B before enter
} // vptr must be set to vtbl of D2 after leave
};
Nhưng vptr là không xác định trong khi xây dựng.Đó là một trong những lý do khiến chức năng ảo gọi trong hàm tạo không được đề xuất.
Nếu vptr trong D2 :: D2() là B và định nghĩa B :: f() bị thiếu, this->f();
sẽ bị lỗi khi dereference trỏ đến chức năng trong vtbl.
Nếu vptr trong D2 :: D2() là B và B sử dụng novtable, this->f();
sẽ sụp đổ khi dereference một vptr uninitialized.
Thực tế, vptr trong D2 :: D2() là D2 trong MSVC (msvc8) .Trình biên dịch thiết lập vptr thành D2 trước khi thực thi mã khác trong D2 :: D2().
Vì vậy, this->f();
gọi D2 :: f() và ba xác nhận sẽ bị vi phạm.
Về cơ bản, điều này sẽ giảm xuống (1) không có cuộc gọi hàm ảo nào trong các hàm tạo và (2) nếu một hàm ảo thuần túy được gọi là bạn không còn nhận được thông tin lỗi nêu rõ "hàm ảo thuần túy được gọi". – paxos1977
nếu tôi hiểu chính xác: Bất kỳ cuộc gọi fn ảo nào bên trong ctor hoặc dtor đều được chuyển đổi thành thời gian biên dịch liên kết. Chúng tôi không thể thực hiện cuộc gọi fn ảo từ (c/d) tors. Lý do là tại thời điểm đối tượng của lớp cơ sở được tạo ra, nó không có kiến thức về lớp dẫn xuất và do đó không thể thực hiện cuộc gọi đến lớp dẫn xuất và các phương thức cùng một logic được áp dụng.
Ngoài ra, hãy xem câu hỏi liên quan của tôi http://stackoverflow.com/questions/1787752/ – paxos1977