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

后端 未结 2 2188
闹比i
闹比i 2021-02-19 14:31

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

2条回答
  •  闹比i
    闹比i (楼主)
    2021-02-19 15:30

    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(&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(this) );
                assert( dynamic_cast(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.

提交回复
热议问题