こんにちはゲストさん。会員登録(無料)して質問・回答してみよう!

解決済みの質問

C++でUNDOを実装しようとしています。

C++でUNDOを実装しようとしています。
処理前にクラスオブジェクトをコピーしておき、
UNDOの処理では処理前のクラスを元のクラスに
かぶせてしまえば処理前の状態に戻ると思うの
です。
ところが、クラスオブジェクトの中でポインタ
変数があり、コピーした内容も書き換わってし
まいます。
いわゆるディープコピーをしなければならない
らしいということまで分かったのですが、その
方法がわかりません。
サンプルなどが掲載されているホームページが
ありましたら、そのURLを教えてください。

投稿日時 - 2010-05-01 07:54:53

QNo.5863446

困ってます

質問者が選んだベストアンサー

 続きです。

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;
}

投稿日時 - 2010-05-04 21:34:11

補足

回答ありがとうございます。
掲載いただいたソースを参考に頑張ってみます。

投稿日時 - 2010-05-15 13:30:40

このQ&Aは役に立ちましたか?

11人が「このQ&Aが役に立った」と投票しています

回答(4)

ANo.4

ディープコピーの話ではないですが。

UNDO/REDOを実装するのは、
処理対象のクラス自体を時系列にそって全て覚えておく
でも、まあいいといえばいいですが
これだと、ものすごくメモリを食います。

普通は、そうではなくて、
処理対象のクラスのインスタンス自体を覚えておくのではなくて、
なんらかの処理そのものを表わすクラス(コマンドクラス)を作って、
これを覚えておきます。UNDOしたいときには、現在の処理対象のクラスに対して、処理を逆に適用します。
つまり、処理対象のクラスの時系列自体を覚えておくのではなくて、その差分だけをおぼえておくというか。

で、プログラムを組んでいると、こういうことをしたくなることはよくあるんで、
デザインパタンという形でまとめられています。
Commandパタンと呼ばれています。
http://www.google.com/search?hl=ja&q=command%E3%83%91%E3%82%BF%E3%83%B3+UNDO+REDO

投稿日時 - 2010-05-10 00:51:22

補足

回答ありがとうございます。
CommandパターンのUndo/Redoについて検討しましたが、
元々のコーディングがまったくその辺のことを考慮して
いない為、今回は適用を断念しました。

投稿日時 - 2010-05-15 13:29:40

ANo.2

 こんばんは。
 そのポインタがバッファを割り当てた物であれば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);
}

投稿日時 - 2010-05-04 21:32:03

ANo.1

クラス構造がどうなってるか分かりませんが、可変長の文字列が有るなら、ポインタの先とかを何処かメモリ上にコピー展開しておくとか、ストリングスオブジェクトをコピーしておかないといけませんよね。

単純では無いと思いますけど、UNDOするストリングスオブジェクト毎に設計する方が良いんじゃないですか?

http://www.geocities.jp/naosacra/mops/Manual/III_2/strings.html

投稿日時 - 2010-05-01 08:44:47

あなたにオススメの質問