釋出
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 的複製建構函式。

值得一提的是,這種技術利用了這樣一個事實:
編譯器在確定派生類虛方法是否重寫了具有相同名稱的基類方法時,
並不考慮函式的返回型別。
並不考慮函式的返回型別。