2

次のような「アクション」純粋抽象クラスを定義しました。

class Action {
 public:
    virtual void execute () = 0;
    virtual void revert () = 0;
    virtual ~Action () = 0;
};

また、ユーザーがクラスで実行できる各コマンドを表しました。

実際の元に戻す/やり直すには、次のようなことをしたいと思います:

元に戻す

Action a = historyStack.pop();
a.revert();
undoneStack.push(a);

やり直し

Action a = undoneStack.pop();
a.execute();
historyStack.push(a);

「アクション」はインスタンス化できない抽象クラスであるため、コンパイラは明らかにこれを受け入れません。

それで、すべてを再設計する必要がありますか、それともこの問題に対する簡単な解決策はありますか?

4

3 に答える 3

8

コンパイラを満足させるために、アクションをポインタとして保存する必要があります。

std::vector<Action*> historyStack;
/*...*/
historyStack.push_back(new EditAction(/*...*/));
Action* a = historyStack.pop();
a->revert();
undoneStack.push(a);

うまくいかない別の理由がありstd::vector<Action> historyStack;、それはスライスです。派生クラスのオブジェクトをベクターに追加すると、それらは基本クラスにキャストされ、すべてのポリモーフィズムが失われます。詳細はこちら:オブジェクトのスライスとは?

編集ptr_vector を使用して、ベクトル内のオブジェクトの有効期間を管理することを検討してください: http://www.boost.org/doc/libs/1_37_0/libs/ptr_container/doc/tutorial.html

于 2010-01-12T00:57:10.030 に答える
0

実行された操作へのポインターをキューに格納する必要があります。

例えば:

std::vector<Action*> historyStack;
std::vector<Action*> undoneStack;

それで:

Action* a = historyStack.pop_back(); 
a->revert(); 
undoneStack.push_back( a ); 

と:

Action* a = undoneStack.pop_back(); 
a->execute(); 
historyStack.push_back(a); 

もちろん、newdeleteを使用して実際のActionオブジェクトのメモリを作成および解放する必要があります。また、標準のコンテナでauto_ptrを使用できないと思うので、メモリを手動で管理するか、他の方法を実装する必要があります。しかし、アンドゥ バッファをクラスでラップする場合、これは大きな問題にはなりません。

于 2010-01-12T00:56:27.517 に答える
0

とにかく、ポリモーフィックディスパッチは、C++ のポインターまたは参照を介してのみ発生します。Action の値を作成できない場合がありますが、Action への参照とポインターを作成できることがわかります。

pop は、アクションへの (おそらくスマートな) ポインターまたは参照を返す必要があるだけです。1 つの方法として、 std::auto_ptr とboost::ptr_dequeを使用することが考えられます。これにより、(正しい使用法で) アクションが後で適切にクリーンアップされることが保証されます。

std::auto_ptr<Action> a = historyStack.pop_front();
a->revert();
undoneStack.push_front(a);

別のオプションは、std::stackboost::shared_ptr<Action>または類似のものである可能性があります。または、生のポインターをそのまま使用することもできますが、所有権が正しく管理されるように注意する必要があります。

于 2010-01-12T01:03:30.240 に答える