Jul*_*n-L 8 c++ prototype clone virtual-copy
我想执行指向多态类的指针的STL容器的"深层副本" .
我知道Prototype设计模式,通过Virtual Ctor Idiom实现,如C++ FAQ Lite,第20.8项中所述.
它简单明了:
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
};
Run Code Online (Sandbox Code Playgroud)
然后是深层复制:
for( i = 0; i < oldVector.size(); ++i )
newVector.push_back( oldVector[i]->clone() );
Run Code Online (Sandbox Code Playgroud)
正如Andrei Alexandrescu 所述:
该
clone()实施必须遵循所有派生类相同的模式; 尽管它具有重复的结构,但没有合理的方法来自动定义clone()成员函数(超出宏,即).
而且,客户ABC可能做坏事.(我的意思是,没有什么能阻止客户做坏事,所以,它会发生.)
我的问题是:是否有另一种方法可以使抽象基类克隆而不需要派生类来编写与克隆相关的代码?(助手类?模板?)
以下是我的背景.希望它有助于理解我的问题.
我正在设计一个类层次结构来对类执行操作Image:
struct ImgOp
{
virtual ~ImgOp() {}
bool run( Image & ) = 0;
};
Run Code Online (Sandbox Code Playgroud)
图像操作是用户定义的:类层次结构的客户端将实现自己的类派生自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 { ... };
...
Run Code Online (Sandbox Code Playgroud)
可以在图像上顺序执行多个操作:
bool do_operations( vector< ImgOp* > v, Image &i )
{
for_each( v.begin(), v.end(),
/* bind2nd( mem_fun( &ImgOp::run ), i ... ) don't remember syntax */ );
}
Run Code Online (Sandbox Code Playgroud)
如果有多个图像,则可以拆分该集合并在多个线程上共享.为了确保"线程安全",每个线程必须拥有自己包含的所有操作对象的副本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 */ );
}
Run Code Online (Sandbox Code Playgroud)
当图像操作类很小时,这是有意义的:不要序列化对ImgOps的唯一实例的访问,而是为每个线程提供自己的副本.
困难的部分是避免新ImgOp衍生类的编写者编写任何与克隆相关的代码.(因为这是实施细节 - 这就是为什么我用奇怪的重复模式驳回保罗的答案.)
您可以使用奇怪的递归模式,但它可能会使您的代码可读性降低.您仍然需要复制构造函数.它的工作原理如下.
struct ABC // Abstract Base Class
{
virtual ~ABC() {}
virtual ABC * clone() const = 0;
};
template <class TCopyableClass>
struct ClonableABC : public ABC
{
virtual ABC* clone() const {
return new TCopyableClass( *(TCopyableClass*)this );
}
};
struct SomeABCImpl : public ClonableABC<SomeABCImpl>
{};
Run Code Online (Sandbox Code Playgroud)
仅供参考,这是我提出的设计。感谢 Paul 和 FredOverflow 的投入。(还有马丁·约克的评论。)
多态性是在编译时使用模板和隐式接口执行的:
template< typename T >
class ImgOp
{
T m_t; // Not a ptr: when ImgOp is copied, copy ctor and
// assignement operator perform a *real* copy of object
ImageOp ( const ImageOp &other ) : m_t( other .m_t ) {}
ImageOp & operator=( const ImageOp & );
public:
ImageOp ( const T &p_t ) : m_t( p_t ) {}
ImageOp<T> * clone() const { return new ImageOp<T>( *this ); }
bool run( Image &i ) const { return m_t.run( i); }
};
// Image operations need not to derive from a base class: they must provide
// a compatible interface
class CheckImageSize { bool run( Image &i ) const {...} };
class CheckImageResolution { bool run( Image &i ) const {...} };
class RotateImage { bool run( Image &i ) const {...} };
Run Code Online (Sandbox Code Playgroud)
现在所有与克隆相关的代码都位于一个唯一的类中。然而,现在不可能ImgOp在不同的操作上模板化 s 的容器:
vector< ImgOp > v; // Compile error, ImgOp is not a type
vector< ImgOp< ImgOp1 > > v; // Only one type of operation :/
Run Code Online (Sandbox Code Playgroud)
添加一个非模板库作为接口:
class AbstractImgOp
{
ImageOp<T> * clone() const = 0;
bool run( Image &i ) const = 0;
};
template< typename T >
class ImgOp : public AbstractImgOp
{
// No modification, especially on the clone() method thanks to
// the Covariant Return Type mechanism
};
Run Code Online (Sandbox Code Playgroud)
现在我们可以写:
vector< AbstractImgOp* > v;
Run Code Online (Sandbox Code Playgroud)
但是操作图像操作对象变得很困难:
AbstractImgOp *op1 = new AbstractImgOp;
op1->w = ...; // Compile error, AbstractImgOp does not have
op2->h = ...; // member named 'w' or 'h'
CheckImageSize *op1 = new CheckImageSize;
op1->w = ...; // Fine
op1->h = ...;
AbstractImgOp *op1Ptr = op1; // Compile error, CheckImageSize does not derive
// from AbstractImgOp? Confusing
CheckImageSize op1;
op1.w = ...; // Fine
op1.h = ...;
CheckImageResolution op2;
// ...
v.push_back( new ImgOp< CheckImageSize >( op1 ) ); // Confusing!
v.push_back( new ImgOp< CheckImageResolution >( op2 ) ); // Argh
Run Code Online (Sandbox Code Playgroud)
基于FredOverflow的解决方案,制作一个克隆指针,使框架更简单使用。
然而,这个指针不需要模板化,因为它被设计为只保存一种类型的 ptr ——只有 ctor 需要模板化:
class ImgOpCloner
{
AbstractImgOp *ptr; // Ptr is mandatory to achieve polymorphic behavior
ImgOpCloner & operator=( const ImgOpCloner & );
public:
template< typename T >
ImgOpCloner( const T &t ) : ptr( new ImgOp< T >( t ) ) {}
ImgOpCloner( const AbstractImgOp &other ) : ptr( other.ptr->clone() ) {}
~ImgOpCloner() { delete ptr; }
AbstractImgOp * operator->() { return ptr; }
AbstractImgOp & operator*() { return *ptr; }
};
Run Code Online (Sandbox Code Playgroud)
现在我们可以写:
CheckImageSize op1;
op1.w = ...; // Fine
op1.h = ...;
CheckImageResolution op2;
// ...
vector< ImgOpCloner > v;
v.push_back( ImgOpCloner( op1 ) ); // This looks like a smart-ptr, this is not
v.push_back( ImgOpCloner( op2 ) ); // confusing anymore -- and intent is clear
Run Code Online (Sandbox Code Playgroud)