发布
2010年1月27日

克隆模式

评分:4.2/5 (29 票)
*****
为了复制一个对象,您必须在编译时知道该
对象的类型,因为类型是复制构造函数的“名称”

1
2
3
4
void copy_me( const std::string& s ) {     
    std::string  s_copy( s );
    std::string* p_s_copy = new std::string( s );
}


我知道在编译时 s 的类型是“std::string”,因为它在参数列表中
明确说明了。但如果 s 的类型是基类呢?

1
2
3
4
5
6
class Base {};
class Derived : public Base {};

void copy_me( const Base& b ) {
    Base b_copy( b );   // ????
}


这并不完全奏效,因为我可以用派生类实例调用 copy_me(),
在这种情况下,该函数将希望实例化一个 Derived 对象,而不是一个 Base 对象。
但在编译时,我根本无法知道这一点。事实上,我甚至可以在一个地方用 Base 实例调用 copy_me(),
在另一个地方用 Derived 实例调用,在第三个地方用派生自 Base 或 Derived 的其他东西调用。
在另一个地方用 Derived 实例调用,在第三个地方用派生自 Base 或 Derived 的其他东西调用。
在第三个地方用派生自 Base 或 Derived 的其他东西调用。

这个问题如何解决?

克隆模式正是为此目的而实现的。克隆模式如下所示
克隆模式如下所示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// Depending upon your needs, you might not require a base class
// clonable concept.  It would only be needed if you need to store
// clonable objects polymorphically.
struct clonable {
    virtual ~clonable() {}
    virtual clonable* clone() const = 0;
};

class Base : public clonable {
  public:
     virtual Base* clone() const
        { return new Base( *this ); }
};

class Derived : public Base {
  public:
     virtual Derived* clone() const
        { return new Derived( *this ); }
};


现在,copy_me 看起来是这样的

1
2
3
4
void copy_me( const Base& b ) {
    Base* clone = b.clone();
    // delete clone;  
};


如果 b 的“真实类型”是 Base,我就成功调用了 Base 的复制构造函数,
如果 b 的“真实类型”是 Derived,我就成功调用了 Derived 的复制构造函数。
如果 b 的“真实类型”是 Derived,我就成功调用了 Derived 的复制构造函数。

值得一提的是,这种技术利用了这样一个事实:
编译器在确定派生类虚方法是否重写了具有相同名称的基类方法时,
并不考虑函数的返回类型。
并不考虑函数的返回类型。