Влияет ли использование __declspec(novtable) на абстрактные базовые классы каким-либо образом на RTTI?
Или есть ли другие известные негативные последствия использования __declspec(novtable)? Я не могу найти ссылки на какие-либо вопросы.
2 ответа
MSCV использует one vptr per object and one vtbl per class
реализовать механизм OO, такой как RTTI и виртуальные функции.
Таким образом, RTTI и виртуальные функции будут работать нормально, если и только если vptr был установлен правильно.
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 должен быть абстрактным классом при использовании __declspec(novtable)
,
Там не будет никакого экземпляра B, кроме как в конструкторе D1.
И __declspec(novtable) в большинстве случаев не оказывает негативного влияния.
Но при построении производного класса __declspec(novtable)
будет отличать его от семантики 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.
}
};
Примечание: виртуальный f () = 0
; делает е быть pure virtual function
и B, чтобы быть абстрактным классом.definition
чисто виртуальной функции could
(не must
) отсутствовать.
C++ допускает вызов виртуальной функции в конструкторе, который мы не рекомендуем.
Обновление: ошибка в D2: vptr в производном конструкторе.
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
};
Но vptr является неопределенным во время конструирования. Это одна из причин того, что вызов виртуальной функции в конструкторе не рекомендуется.
Если vptr в D2::D2() был B, а определение B::f() отсутствовало, this->f();
вылетает при разыменовании указателя на функцию в vtbl.
Если vptr в D2::D2() был B и B использовать novtable, this->f();
вылетит при разыменовании неинициализированного vptr.
Фактически, vptr в D2::D2() - это D2 в MSVC(msvc8). Компилятор установил vptr в D2 перед выполнением другого кода в D2::D2().
Так this->f();
вызывает D2::f() и три утверждения будут нарушены.
Если я правильно понимаю: любой виртуальный вызов fn внутри ctor или dtor преобразуется в компиляцию во время компиляции. Мы не можем делать виртуальные вызовы FN из (c/d) торов. Причина в том, что во время создания объекта базового класса он не знает производного класса и, следовательно, не может выполнить вызов производного класса и без использования той же логики.