MSVC中的虚拟继承错误

时间:2010-02-10 22:02:07

标签: c++ visual-studio

似乎我的问题是MSVC中的一个错误。我正在使用带有Service Pack 1的Visual Studio 2008,我的代码适用于GCC(在codepad.org上测试过)。

有关此错误的任何官方信息?任何想法如何解决它? VS2010中是否修复了该错误?我们将非常感谢所有见解。

代码:

struct Base {
    Base(int i = 0) : i(i) {}
    virtual ~Base() {}
    virtual Base *clone() const = 0;

protected:
    int i;
};

struct A : virtual public Base {
    A() {}
    virtual A *clone() const = 0;
};

struct B : public A {
    B() {}
    B *clone() const { return new B(*this); }

    /// MSVC debugger shows that 'b' is for some reason missing the Base
    /// portion of it's object ("Error: expression cannot be evaluated")
    /// and trying to access 'b.i' causes an unhandled exception.
    ///
    /// Note: This only seems to occur with MSVC
    B(const B &b) : Base(b.i), A() {}
};

void foo(const A &elem) {
    A *a = elem.clone();
    if (a) delete a;
}

int main() {
    A *a = new B;
    foo(*a);
    delete a;
}

2 个答案:

答案 0 :(得分:8)

在通过this进行调用时,好像编译器没有正确调整A::clone指针。如果你删除了A::clone的声明,那么一切正常。

深入挖掘,当你有A::clone时,vtable看起来像这样:

    [0x0]   0x002f1136 [thunk]:B::`vector deleting destructor'`vtordisp{4294967292,0}' (unsigned int)   void *
    [0x1]   0x002f11e0 [thunk]:B::clone`vtordisp{4294967292,0}' (void)  void *
    [0x2]   0x002f12ad [thunk]:B::clone`vtordisp{4294967292,4}' (void)  void *
    [0x3]   0x002f12a3 B::clone(void)   void *

foo调用elem.__vfptr[2],错误地将this错误地偏移-4个字节。没有A::clone,vtable看起来像这样:

    [0x0]   0x00ee1136 [thunk]:B::`vector deleting destructor'`vtordisp{4294967292,0}' (unsigned int)   void *
    [0x1]   0x00ee11e0 [thunk]:B::clone`vtordisp{4294967292,0}' (void)  void *
    [0x2]   0x00ee12a3 B::clone(void)   void *

foo调用elem.__vfptr[1]。这根本不会调整this(并且代码假定this将等于Base而不是B。)

所以看起来编译器假设A::clone是一个新的虚方法,并且在确定Base::clone是否需要新的虚拟表时不会覆盖A,而是其他一些代码稍后确定A不需要虚拟表。您可以通过比较sizeof(B)是否有新的虚函数来验证这一点:

struct A : virtual public Base {
    A() {}
    virtual A *clone() const = 0;
}; //sizeof(B)==16

struct A : virtual public Base {
    A() {}
    virtual A *clone() const = 0;
virtual const A *clone2() const { return this; }
}; //sizeof(B)==20

所以这是一个编译错误。

答案 1 :(得分:1)

从某些测试中可以看出,该错误是由虚拟基类与使用协变返回类型的纯虚方法的组合引起的。

由于从Base类中删除纯虚方法,或者使Base成为非虚基类或使clone()方法不协变似乎解决了这个错误。

我想这个问题已经解决了(在我向MS提交错误报告之后),我甚至还有一些选项可以绕过它。 :)