Domanda

Capisco perché chiamare una funzione virtuale da un costruttore è male, ma io non sono sicuro perché la definizione di un distruttore si tradurrebbe in un "metodo virtuale puro chiamato" eccezione. Il codice utilizza valori const per ridurre l'uso di allocazione dinamica -. Eventualmente anche il colpevole

#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;
}
È stato utile?

Soluzione

Comportamento Hai indefinito. Per quanto il parametro da ctor di Button è un const & da una temporanea, viene distrutto alla fine di quella linea, subito dopo le finiture ctor. Successivamente utilizzare _action, dopo dtor di azione ha già eseguito. Poiché si tratta di UB, l'implementazione è permesso di lasciare che accada nulla , ea quanto pare l'implementazione capita di fare qualcosa di leggermente diverso a seconda se si dispone di un dtor banale ActionBase o meno. Si ottiene il messaggio "puro virtuale chiamato" perché l'implementazione fornisce un comportamento per chiamare ActionBase :: Invoke direttamente, che è ciò che accade quando l'attuazione puntatore vtable dell'oggetto in dtor di azione.

Mi consiglia di utilizzare boost.function o una simile libreria 'azione callback' (spinta ha segnali e signals2 , ad esempio ).

Altri suggerimenti

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.

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

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top