代码之家  ›  专栏  ›  技术社区  ›  oz10

在抽象基类上使用declspec(novtable)会以任何方式影响RTTI吗?

  •  11
  • oz10  · 技术社区  · 16 年前

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

    2 回复  |  直到 14 年前
        1
  •  10
  •   OwnWaterloo    16 年前

    MSCV使用 one vptr per object and one vtbl per class 实现诸如RTTI和虚拟函数等OO机制。
    因此,只有正确设置了vptr,rtti和virtual函数才能正常工作。

    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) .
    除了在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成为a 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是不确定的,这也是不推荐在构造函数中调用虚函数的原因之一。

    如果d2::d2()中的vptr为b,并且缺少b::f()的定义, this->f(); 当取消引用指向vtbl中函数的指针时将崩溃。
    如果d2::d2()中的vptr是b和b,请使用novtable, 这个& gt;f-(); 在取消对未初始化的vptr的引用时将崩溃。

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

        2
  •  3
  •   gangs    14 年前

    如果我理解正确: ctor或dtor中的任何虚拟fn调用都转换为编译时链接。我们无法从(C/D)TOR进行虚拟FN调用。原因是,在创建基类的对象时,它不知道派生类,因此无法调用派生类和W.R.T,应用相同的逻辑。