【问题标题】:C++: inexplicable "pure virtual function call" errorC++:莫名其妙的“纯虚函数调用”错误
【发布时间】:2016-12-06 20:14:37
【问题描述】:

我在使用 Microsoft Visual C++ 2015 时遇到了一些困难,并且能够通过一个小程序来重现该问题。给定以下类:

class BaseClass {
public:
    BaseClass()
        : mValue( 0 )
        , mDirty( true )
    {}
    virtual ~BaseClass() {}
    virtual int getValue() const { if( mDirty ) updateValue(); return mValue; }

protected:
    virtual void updateValue() const = 0;

    mutable bool mDirty;
    mutable int  mValue;
};

class DerivedClass : public BaseClass {
public:
    DerivedClass() {}

protected:
    void updateValue() const override
    {
        mValue++;
        mDirty = false;
    }
};

class Impersonator {
public:
    Impersonator() {}

    // conversion operator
    operator DerivedClass() const
    {
        return DerivedClass();
    }

    // conversion method
    DerivedClass toDerived() const
    {
        return DerivedClass();
    }
};

执行以下操作时出现“纯虚函数调用”错误:

void use( const BaseClass &inst )
{
    // calls `getValue` which in turns calls the virtual function 'updateValue'
    int value = inst.getValue();
}

int main()
{
    // creates a temporary, then passes it by reference:
    use( DerivedClass() ); // this works

    // calls conversion operator to create object on stack, then passes it by reference:
    DerivedClass i = Impersonator();
    use( i ); // this works

    // calls conversion method to create a temporary, then passes it by reference:
    use( Impersonator().toDerived() ); // this works

    // calls conversion operator to create a temporary, then passes it by reference:
    Impersonator j = Impersonator();
    use( j ); // causes a pure virtual function call error!

    return 0;
}

鉴于我无法更改 void use(const BaseClass&) 函数,我可以更改 Impersonator 类中的任何内容以允许使用最后一次调用而不产生调试错误吗?

【问题讨论】:

  • 如果你在最后一次调用 getValue 中设置断点并检查 vtable 指针,MSVC 认为你有一个 BaseClass 对象,看起来不正确。
  • Cannot reproduce。 MVC 被认为不好。
  • 检查生成的程序集表明,出于某种原因或其他 MSVC 决定调用 BaseClass::BaseClass 以复制从 operator DerivedClass 返回的临时值,尽管 BaseClass 是抽象的。将复制构造函数显式声明为非公共使 MSVC 抱怨:error C2248: 'BaseClass::BaseClass' : cannot access protected member declaration in class 'BaseClass'.
  • @AndreyChernyakhovskiy 我不知道。我构建了一个小的test case。旧的 gcc 和 msvc 打印“gotcha”,新的 gcc 和 clang 没有。为什么旧的 gcc 和 msvc 要从 Derived 中复制构造 Base?
  • @AndreyChernyakhovskiy MSVC 绝对是错误的,因为 BaseClass 是抽象的,并且永远不应创建此类的对象。如果复制构造在某种程度上是正确的操作(这对我来说似乎完全不可思议)MSVC 应该抱怨 BaseClass 是抽象的,而不是默默地继续复制构造它。

标签: c++ c++11 base-class pure-virtual visual-studio-2015


【解决方案1】:

我看到的缓解问题的唯一方法是将operator const BaseClass&() 添加到Impersonator 并让它返回对DerivedClass 的引用。

这将创建比编译器尝试使用的有问题/错误的转换更好的转换。

Impersonator 自然无法按值返回并创建临时对象,因此它必须拥有一个 DerivedClass 对象或许多对象,并在适当的时间以某种方式处置它们。此演示程序最简单的方法是让它返回对其数据成员的引用,但真正的程序可能需要做其他事情。

class Impersonator {
public:
    Impersonator() {}

    // conversion operator
    operator DerivedClass()
    {
        return d;
    }
    operator const BaseClass&()
    {
        return d;
    }

private:
    DerivedClass d;
};

【讨论】:

  • 是的,这行得通。不幸的是,实际应用程序中的“模仿者”是一个试图模仿相机的 LightSource 类,它的数据成员应该严格遵循 std140 布局规则,所以我不能添加 DerivedClass 数据成员。我可以在转换运算符中创建一个静态的,但是只有一个这样的“临时”可以处于活动状态,而且它也不是线程安全的。不过,感谢您的意见。
  • 部分解决方案:如果我使用Impersonator 上的方法来创建DerivedClass,而不是转换运算符,它可以工作:DerivedClass toDerived() const { return DerivedClass(); }。我将把它添加到示例代码中。
  • 您可以动态分配 DerivedClass 对象,并将它们放置在线程本地容器中。当您知道可能不需要任何 DerivedClass 对象时,请清理它。或者,如果可以包装use,则删除包装器中的动态对象。
【解决方案2】:

这是一种解决方法。为接受const DerivedClass&use 创建一个包装器。

//I get a "pure virtual function call" error when I do the following :
void use(const BaseClass &inst)
{
    // calls `getValue` which in turns calls the virtual function 'updateValue'
    int value = inst.getValue();
}

void use(const DerivedClass &inst) {
    use(static_cast<const BaseClass&>(inst));
}

更好的匹配意味着将选择解决方法包装器,因此将创建一个正确类型的临时文件,并将对该文件的引用传递给真正的use 实现。

【讨论】:

    猜你喜欢
    • 2019-02-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多