ポリモーフィック クラスへのポインタの STL コンテナの「ディープ コピー」を実行したいと考えています。
C++ FAQ Lite の項目 20.8 で説明されているように、 Virtual Ctor Idiomによって実装されたPrototypeデザイン パターンについて知っています。
それは単純明快です:
struct ABC // Abstract Base Class
{
virtual ~ABC() {}
virtual ABC * clone() = 0;
};
struct D1 : public ABC
{
virtual D1 * clone() { return new D1( *this ); } // Covariant Return Type
};
ディープコピーは次のとおりです。
for( i = 0; i < oldVector.size(); ++i )
newVector.push_back( oldVector[i]->clone() );
欠点
Andrei Alexandrescuは次のように述べています。
実装は
clone()
、すべての派生クラスで同じパターンに従う必要があります。その繰り返し構造にもかかわらず、メンバー関数の定義を自動化する合理的な方法はありませんclone()
(つまり、マクロを超えて)。
さらに、 のクライアントはABC
何か悪いことをする可能性があります。(つまり、クライアントが何か悪いことをするのを妨げるものは何もないので、それは起こります.)
より良いデザイン?
私の質問は次のとおりです。派生クラスにクローン関連のコードを記述させることなく、抽象基本クラスをクローン可能にする別の方法はありますか? (ヘルパー クラス? テンプレート?)
以下は私の文脈です。うまくいけば、それは私の質問を理解するのに役立ちます.
クラスで操作を実行するためのクラス階層を設計していますImage
:
struct ImgOp
{
virtual ~ImgOp() {}
bool run( Image & ) = 0;
};
画像操作はユーザー定義です。クラス階層のクライアントは、以下から派生した独自のクラスを実装しますImgOp
。
struct CheckImageSize : public ImgOp
{
std::size_t w, h;
bool run( Image &i ) { return w==i.width() && h==i.height(); }
};
struct CheckImageResolution { ... };
struct RotateImage { ... };
...
イメージに対して複数の操作を連続して実行できます。
bool do_operations( vector< ImgOp* > v, Image &i )
{
for_each( v.begin(), v.end(),
/* bind2nd( mem_fun( &ImgOp::run ), i ... ) don't remember syntax */ );
}
複数の画像がある場合、セットを分割して複数のスレッドで共有できます。「スレッドセーフ」を保証するために、各スレッドは、含まれているすべての操作オブジェクトの独自のコピーをv
持っている必要があります--v
は、各スレッドでディープ コピーされるプロトタイプになります。
編集済み:スレッドセーフ バージョンでは、Prototype デザイン パターンを使用して、ptrs ではなく、ポイント先オブジェクトのコピーを強制します。
struct ImgOp
{
virtual ~ImgOp() {}
bool run( Image & ) = 0;
virtual ImgOp * clone() = 0; // virtual ctor
};
struct CheckImageSize : public ImgOp { /* no clone code */ };
struct CheckImageResolution : public ImgOp { /* no clone code */ };
struct RotateImage : public ImgOp { /* no clone code */ };
bool do_operations( vector< ImgOp* > v, Image &i )
{
// In another thread
vector< ImgOp* > v2;
transform( v.begin(), v.end(), // Copy pointed-to-
back_inserter( v2 ), mem_fun( &ImgOp::clone ) ); // objects
for_each( v.begin(), v.end(),
/* bind2nd( mem_fun( &ImgOp::run ), i ... ) don't remember syntax */ );
}
これは、イメージ操作クラスが小さい場合に意味があります。 の一意のインスタンスへのアクセスをシリアル化せずImgOp
、各スレッドに独自のコピーを提供します。
難しいのは、新しいImgOp
派生クラスの作成者がクローン関連のコードを作成しないようにすることです。(これは実装の詳細であるためです。これが、興味深い繰り返しパターンでポールの回答を却下した理由です。)