纯虚方法调用

发布于 2024-08-16 17:10:59 字数 1068 浏览 4 评论 0原文

我理解为什么从构造函数调用虚函数是不好的,但我不确定为什么定义析构函数会导致“纯虚方法调用”异常。该代码使用 const 值来减少动态分配的使用 - 也可能是罪魁祸首。

#include <iostream>
using namespace std;

class ActionBase {
 public:
    ~ActionBase() { } // Comment out and works as expected

    virtual void invoke() const = 0;
};

template <class T>
class Action : public ActionBase {
 public:
    Action( T& target, void (T::*action)())
     : _target( target ), _action( action ) { }

    virtual void invoke() const {
        if (_action) (_target.*_action)();
    }

    T&   _target;
    void (T::*_action)();
};

class View {
 public:
    void foo() { cout << "here" << endl; }
};

class Button : public View {
 public:
    Button( const ActionBase& action )
     : _action( action ) { }

    virtual void mouseDown() {
        _action.invoke();
    }

 private:
    const ActionBase& _action;
};

int main( int argc, char* argv[] )
{
    View view;
    Button button = Button( Action<View>( view, &View::foo ) );
    button.mouseDown();

    return 0;
}

I understand why calling a virtual function from a constructor is bad, but I'm not sure why defining a destructor would result in a "pure virtual method called" exception. The code uses const values to reduce the use of dynamic allocation - possibly also the culprit.

#include <iostream>
using namespace std;

class ActionBase {
 public:
    ~ActionBase() { } // Comment out and works as expected

    virtual void invoke() const = 0;
};

template <class T>
class Action : public ActionBase {
 public:
    Action( T& target, void (T::*action)())
     : _target( target ), _action( action ) { }

    virtual void invoke() const {
        if (_action) (_target.*_action)();
    }

    T&   _target;
    void (T::*_action)();
};

class View {
 public:
    void foo() { cout << "here" << endl; }
};

class Button : public View {
 public:
    Button( const ActionBase& action )
     : _action( action ) { }

    virtual void mouseDown() {
        _action.invoke();
    }

 private:
    const ActionBase& _action;
};

int main( int argc, char* argv[] )
{
    View view;
    Button button = Button( Action<View>( view, &View::foo ) );
    button.mouseDown();

    return 0;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

灼痛 2024-08-23 17:10:59

你有未定义的行为。因为 Button 的 ctor 的参数是一个 const& 。从暂时来看,它会在该行的末尾,即 ctor 完成后被销毁。稍后,在 Action 的 dtor 已经运行之后,您可以使用 _action。由于这是 UB,允许任何事情发生,并且显然您的实现会根据 ActionBase 中是否有一个简单的 dtor 执行一些稍微不同的操作。您会收到“纯虚拟调用”消息,因为该实现提供了直接调用 ActionBase::invoke 的行为,这就是当实现更改 Action 的 dtor 中对象的 vtable 指针时会发生的情况。

我建议使用 boost.function 或类似的“动作回调”库(boost 有 signals< /a> 和 signals2,例如)。

You have Undefined Behavior. As the parameter to Button's ctor is a const& from a temporary, it is destroyed at the end of that line, right after the ctor finishes. You later use _action, after Action's dtor has already run. Since this is UB, the implementation is allowed to let anything happen, and apparently your implementation happens to do something slightly different depending on whether you have a trivial dtor in ActionBase or not. You get the "pure virtual called" message because the implementation is providing behavior for calling ActionBase::invoke directly, which is what happens when the implementation changes the object's vtable pointer in Action's dtor.

I recommend using boost.function or a similar 'action callback' library (boost has signals and signals2, for example).

眼趣 2024-08-23 17:10:59

在析构函数上设置断点,就会清楚发生了什么。是的,您正在传递一个 Action<> 的临时实例。到按钮构造函数。它在按钮构造运行后被销毁。像这样编写,问题就消失了:

View view;
Action<View> event(view, &View::foo);
Button button = Button( event ); 
button.mouseDown();

嗯,这不是一个实际的解决方案,事件不会在真正的 mouseDown 调用范围内。 Button 构造函数必须创建“事件”参数的副本,或者必须管理指向委托的指针。

Set a breakpoint on the destructor and it will become clear what is happening. Yup, you are passing a temporary instance of Action<> to the Button constructor. It is destroyed after the button construct runs. Write it like this and the problem disappears:

View view;
Action<View> event(view, &View::foo);
Button button = Button( event ); 
button.mouseDown();

Well, that's not a practical solution, event is not going to be in scope for a real mouseDown invocation. The Button constructor is going to have to create a copy of the "event" argument or it is going to have to manage a pointer to the delegate.

﹉夏雨初晴づ 2024-08-23 17:10:59

具有虚函数的类应该始终具有虚析构函数,因此 ~ActionBase() 应该是虚函数(~Action() 也应该如此)。如果您打开更多编译器警告,您将收到有关此的警告。

本质上,由于查找规则,析构函数被调用用于编译器知道无法实例化的类型(纯虚拟),因此它知道一定出了问题。

我相信其他人可以比我更好地解释:)

A class with virtual functions should always have a virtual destructor, so ~ActionBase() should be virtual, (and so should ~Action()). If you turn on more compiler warning you will get a warning about this.

Essentially, because of the lookup rules, the destructor is called for a type that the compiler knows cannot be instantiated (pure virtual), so it knows something must have gone wrong.

I'm sure someone else can explain better than I can :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文