【问题标题】:Does using __declspec(novtable) on abstract base classes affect RTTI in any way?在抽象基类上使用 __declspec(novtable) 是否会以任何方式影响 RTTI?
【发布时间】:2010-12-20 13:13:44
【问题描述】:

或者,使用 __declspec(novtable) 是否有任何其他已知的负面影响?我似乎找不到任何问题的参考。

【问题讨论】:

标签: c++ visual-studio visual-c++ rtti compiler-specific


【解决方案1】:

如果我理解正确: ctor 或 dtor 内的任何虚拟 fn 调用都将转换为编译时链接。我们不能从 (c/d)tors 进行虚拟 fn 调用。原因是在创建基类的对象时,它不知道派生类,因此无法调用派生类,并且对 dtors 应用相同的逻辑。

【讨论】:

    【解决方案2】:

    MSCV使用one vptr per object and one vtbl per class实现RTTI、虚函数等OO机制。
    所以当且仅当 vptr 设置正确时,RTTI 和虚函数才能正常工作。

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

    使用__declspec(novtable)时,B应该是一个抽象类。
    除了 D1 的构造函数之外,不会有 B 的实例。
    并且 __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;使 f 成为 pure virtual function 和 B 成为抽象类。
    缺少纯虚函数could(不是must)的definition
    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在构造过程中是不确定的,这也是不推荐在构造函数中调用虚函数的原因之一。

    如果 D2::D2() 中的 vptr 为 B,并且缺少 B::f() 的定义,则 this-&gt;f(); 将在 vtbl 中取消引用指向函数的指针时崩溃。
    如果 D2::D2() 中的 vptr 是 B 并且 B 使用 novtable,则 this-&gt;f(); 将在取消引用未初始化的 vptr 时崩溃。

    实际上,D2::D2() 中的 vptr 是 MSVC(msvc8) 中的 D2。编译器在执行 D2::D2() 中的其他代码之前将 vptr 设置为 D2。
    所以this-&gt;f(); 调用 D2::f() 会违反这三个断言。

    【讨论】:

    • 基本上,这归结为 (1) 构造函数中没有虚函数调用,以及 (2) 如果实际调用了纯虚函数,您将不再收到说明“已调用纯虚函数”的错误信息.
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-22
    • 2018-07-26
    • 1970-01-01
    • 2011-12-07
    • 1970-01-01
    相关资源
    最近更新 更多