5

コンストラクタから仮想関数を呼び出すことがなぜ悪いのかは理解していますが、デストラクタを定義すると「呼び出される純粋仮想メソッド」例外が発生する理由がわかりません。コードは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;
}
4

3 に答える 3

7

未定義動作があります。 Buttonのctorへのパラメーターは一時的なものからのconst&であるため、ctorが終了した直後のその行の終わりで破棄されます。Actionのdtorがすでに実行された後、後で_actionを使用します。これはUBであるため、実装は何でも発生させることができます。また、ActionBaseに些細なdtorがあるかどうかによって、実装が少し異なることを行うように見えます。実装がActionBase::invokeを直接呼び出すための動作を提供しているため、「純粋な仮想呼び出し」メッセージが表示されます。これは、実装がActionのdtorでオブジェクトのvtableポインターを変更したときに発生します。

boost.functionまたは同様の「アクションコールバック」ライブラリを使用することをお勧めします(たとえば、 boostにはsignalssignals2があります)。

于 2010-01-01T01:15:13.230 に答える
3

デストラクタにブレークポイントを設定すると、何が起こっているのかが明らかになります。はい、Action<>の一時インスタンスをButtonコンストラクターに渡します。ボタン構成が実行された後、破棄されます。このように書くと、問題はなくなります。

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

まあ、それは実用的な解決策ではありません。イベントは実際のmouseDown呼び出しの範囲内にはなりません。Buttonコンストラクターは、「event」引数のコピーを作成するか、デリゲートへのポインターを管理する必要があります。

于 2010-01-01T01:22:08.147 に答える
2

仮想関数を持つクラスには、常に仮想デストラクタが必要であるため、仮想である~ActionBase()必要があります(また、そうする必要があります~Action())。さらにコンパイラ警告をオンにすると、これに関する警告が表示されます。

基本的に、ルックアップルールのために、コンパイラがインスタンス化できない(純粋な仮想)ことがわかっているタイプに対してデストラクタが呼び出されるため、何かが間違っている必要があることがわかります。

他の誰かが私よりもうまく説明できると確信しています:)

于 2010-01-01T00:54:20.513 に答える