Подтвердить что ты не робот

Использует ли __declspec (novtable) абстрактные базовые классы каким-либо образом влияет на RTTI?

Или существуют ли какие-либо другие отрицательные аффекты использования __declspec (novtable)? Я не могу найти ссылки на какие-либо вопросы.

4b9b3361

Ответ 1

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 С++.

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.
    }
};

Примечание: virtual f() = 0; делает f таким, чтобы быть pure virtual function, а B - абстрактным классом.
definition чистой виртуальной функции could (не must) отсутствует.
С++ разрешает вызов виртуальной функции в конструкторе, который мы не рекомендуем.

Обновление: Ошибка в 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(), и три утверждения будут нарушены.

Ответ 2

если я правильно понимаю: Любой виртуальный вызов fn внутри ctor или dtor преобразуется в компоновку времени компиляции. Мы не можем делать виртуальные вызовы fn из (c/d) торов. Причина в том, что во время создания объекта базового класса он не знает о производном классе и, следовательно, не может сделать вызов производному классу, а w.r.t dtors применяется одна и та же логика.