- ベストアンサー
C++でUNDOを実装しようとしています。
C++でUNDOを実装しようとしています。 処理前にクラスオブジェクトをコピーしておき、 UNDOの処理では処理前のクラスを元のクラスに かぶせてしまえば処理前の状態に戻ると思うの です。 ところが、クラスオブジェクトの中でポインタ 変数があり、コピーした内容も書き換わってし まいます。 いわゆるディープコピーをしなければならない らしいということまで分かったのですが、その 方法がわかりません。 サンプルなどが掲載されているホームページが ありましたら、そのURLを教えてください。
- みんなの回答 (4)
- 専門家の回答
質問者が選んだベストアンサー
続きです。 void test() { std::list<CObject> l; CObject a(3, "CObject - 1"); for(std::size_t n = 0; n < a.size(); ++n) a[n] = CPoint(n, n); ::Print(a); std::cout << a.GetName() << "を退避" << std::endl; l.push_back(a); a = CObject(5, "CObject - 2"); for(std::size_t n = 0; n < a.size(); ++n) a[n] = a[n] + CPoint(n + 2, n + 1); ::Print(a); std::cout << a.GetName() << "を退避" << std::endl; l.push_back(a); a = l.front(); std::cout << a.GetName() << "を復元" << std::endl; ::Print(a); } int main() { ::test(); //VC++特有 //const int result = ::_CrtDumpMemoryLeaks(); return 0; }
その他の回答 (3)
- rabbit_cat
- ベストアンサー率40% (829/2062)
ディープコピーの話ではないですが。 UNDO/REDOを実装するのは、 処理対象のクラス自体を時系列にそって全て覚えておく でも、まあいいといえばいいですが これだと、ものすごくメモリを食います。 普通は、そうではなくて、 処理対象のクラスのインスタンス自体を覚えておくのではなくて、 なんらかの処理そのものを表わすクラス(コマンドクラス)を作って、 これを覚えておきます。UNDOしたいときには、現在の処理対象のクラスに対して、処理を逆に適用します。 つまり、処理対象のクラスの時系列自体を覚えておくのではなくて、その差分だけをおぼえておくというか。 で、プログラムを組んでいると、こういうことをしたくなることはよくあるんで、 デザインパタンという形でまとめられています。 Commandパタンと呼ばれています。 http://www.google.com/search?hl=ja&q=command%E3%83%91%E3%82%BF%E3%83%B3+UNDO+REDO
補足
回答ありがとうございます。 CommandパターンのUndo/Redoについて検討しましたが、 元々のコーディングがまったくその辺のことを考慮して いない為、今回は適用を断念しました。
- machongola
- ベストアンサー率60% (434/720)
こんばんは。 そのポインタがバッファを割り当てた物であればSTLのvector辺りで代用すれば、悩む必要もなくなるのでは。 以下はディープコピーです。コピーコンストラクタで処理をします。参考程度で。 #include<stdexcept> #include<iostream> #include<string> #include<list> #include<algorithm> struct CPoint { explicit CPoint(int x = 0, int y = 0) : x(x), y(y){ } int x; int y; }; const CPoint operator + (const CPoint& l, const CPoint& r) { return CPoint(l.x + r.x, l.y + r.y); } struct CObject { explicit CObject(std::size_t size = 1, const std::string& name = std::string("CObject")) : m_name(name), m_size(std::max(1U, std::min(size, 256U))), m_ppt(new CPoint[m_size]) { } CObject(const CObject& r) : m_name(r.m_name), m_size(r.m_size) { //ココがディープコピー this->m_ppt = new CPoint[this->m_size]; std::copy(r.begin(), r.end(), this->begin()); } CObject& operator = (const CObject& r) { if(&r == this)return *this; this->~CObject(); new (this) CObject(r); return *this; } ~CObject() { if(this->m_ppt == NULL)return; delete this->m_ppt; this->m_ppt = NULL; } void SetName(const std::string& name){ this->m_name = name; } const std::string GetName() const { return this->m_name; } std::size_t size() const { return this->m_size; } CPoint* begin(){ return this->m_ppt; } CPoint* end(){ return this->m_ppt + this->m_size; } const CPoint* begin() const { return this->m_ppt; } const CPoint* end() const { return this->m_ppt + this->m_size; } CPoint& operator [](std::size_t n) { return const_cast<CPoint&>(static_cast<const CObject&>(*this)[n]); } const CPoint& operator[](std::size_t n) const { if(n >= this->m_size)throw std::range_error("error"); return this->m_ppt[n]; } private: std::size_t m_size; CPoint* m_ppt; std::string m_name; }; void PrintPoint(const CPoint& p) { std::cout << "[x : " << p.x << "][y : " << p.y << "]" << std::endl; } void Print(const CObject& a) { std::cout << a.GetName() << "を表示" << std::endl; std::for_each(a.begin(), a.end(), &::PrintPoint); }
- t_nojiri
- ベストアンサー率28% (595/2071)
クラス構造がどうなってるか分かりませんが、可変長の文字列が有るなら、ポインタの先とかを何処かメモリ上にコピー展開しておくとか、ストリングスオブジェクトをコピーしておかないといけませんよね。 単純では無いと思いますけど、UNDOするストリングスオブジェクト毎に設計する方が良いんじゃないですか? http://www.geocities.jp/naosacra/mops/Manual/III_2/strings.html
補足
回答ありがとうございます。 掲載いただいたソースを参考に頑張ってみます。