Does using __declspec(novtable) on abstract base classes affect RTTI in any way?

8.2k views Asked by At

Or, are there any other known negative affects of employing __declspec(novtable)? I can't seem to find references to any issues.

2

There are 2 answers

1
OwnWaterloo On BEST ANSWER

MSCV uses one vptr per object and one vtbl per class to implement OO mechanism such as RTTI and virtual functions.
So RTTI and virtual functions will work fine if and only if the vptr has been set correctly.

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 should be an abstract class when use __declspec(novtable).
There will be no instance of B except in the constructor of D1.
And __declspec(novtable) has no negative affects in most case.

But during the construction of derived class __declspec(novtable) will make it different from ISO C++ semantic.

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

Note: virtual f() = 0; makes f to be a pure virtual function and B to be an abstract class.
The definition of a pure virtual function could (not must) be missing.
C++ allows virtual function call in constructor which we don't recommend.

Update: A mistake in D2 : the vptr in derived constructor.

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

But vptr is indeterminate during the construction.It's one of the reason that virtual function call in constructor aren't recommend .

If vptr in D2::D2() was B and definition of B::f() was missing, this->f(); will crash when dereference pointer-to-function in the vtbl.
If vptr in D2::D2() was B and B use novtable, this->f(); will crash when dereference an uninitialized vptr.

In fact , vptr in D2::D2() is D2 in MSVC(msvc8).The compiler set vptr to D2 before execute other code in D2::D2().
So this->f(); calls D2::f() and the three assertions will be violated.

0
gangs On

if I understand it correctly: Any virtual fn call inside a ctor or a dtor is converted to a compile time linking. We cannot make virtual fn calls from the (c/d)tors. The reason is that at the time the object of the base class is getting created it has no knowledge of the derived class and hence cannot make the call to derived class and w.r.t the dtors the same logic applies.