CRTP(Curiously Recurring Template Pattern)を使ってCloneable(Deep copy)を楽に書く方法を考えていた
問題
オブジェクトの深いコピーを作る時は、コピーコンストラクタを内部で呼び出してnewするようなclone関数を作成するのがセオリーだと思い、以下のように書くわけです。
#include <iostream> //適当なクラス class Hoge { public: Hoge* clone(){return new Hoge(*this);} }; class Moge { public: Moge* clone(){return new Moge(*this);} }; int main() { Hoge x1; Moge y1; //クローンしたオブジェクト Hoge *x2 = x1.clone(); Moge *y2 = y1.clone(); delete x2; delete y2; return 0; }
いちいち書くのが面倒くさい
ただ、上の書き方だとDeep copyさせたいクラスを追加するたびに、いちいちclone関数を各クラスに書かねばならず、面倒くさい。
また仮に「コピー可能クラス・インターフェイス」を作成、継承させてもこの問題は変わらなくて、しかも今の場合、本質的に関係ないと思ってるHoge・Mogeクラスが共にCloneableクラスのオブジェクトとして、格納可能になってしまうのはなんか気持ちが悪い。
一応、書くと以下のような感じか。
#include <iostream> //適当なクラス class Cloneable { public: virtual Cloneable* clone(){return new Cloneable(*this);} }; class Hoge : public Cloneable { public: Hoge* clone(){return new Hoge(*this);} }; class Moge : public Cloneable { public: Moge* clone(){return new Moge(*this);} }; int main() { Hoge x1; Moge y1; //クローンしたオブジェクト Hoge *x2 = x1.clone(); Moge *y2 = y1.clone(); //Cloneable *x3 = x1.clone(); //Cloneable *y3 = y1.clone(); //x3 = y3 が出来てしまう delete x2; delete y2; return 0; }
この書き方は
とほぼ同じだと思う。
CRTPを使って対処してみる
ここでこのめんどくさい問題に対処するためにはCRTP(Curiously Recurring Template Pattern)を使うのがいいんじゃないかなと考えて書いてみたのが以下のコード。
継承先に何も書かなくてもcloneする事が出来てうれしいなと思うわけです。
もっと良いやり方があるのでしょうかね?
#include <iostream> //適当なクラス template <class T> class Cloneable { public: T* clone(){return new T(static_cast<T&>(*this));} }; class Hoge : public Cloneable<Hoge> {}; class Moge : public Cloneable<Moge> {}; int main() { Hoge x1; Moge y1; //クローンしたオブジェクト Hoge *x2 = x1.clone(); Moge *y2 = y1.clone(); delete x2; delete y2; return 0; }
いや、そもそも・・・
テンプレート関数一発書いておけばいいって話?どう使い分けるのが良いのだろうか。
よくわからなくなってきた。
#include <iostream> //適当なクラス template <class T> T* clone(const T& x){return new T(x);} class Hoge{}; class Moge{}; int main() { Hoge x1; Moge y1; //クローンしたオブジェクト Hoge *x2 = clone(x1); Moge *y2 = clone(y1); delete x2; delete y2; return 0; }