2009-01-27 35 views
5

假設我們有下面的類層次結構:請在C++中未知的具體類型的副本

class Base { 
    ... 
}; 

class Derived1 : public Base { 
    ... 
}; 

class Derived2 : public Base { 
    ... 
}; 

給定一個Base*這可能指向任何一個Derived1Derived2對象我怎樣才能使實際的副本對象,因爲它的具體類型是未知的。我想過定義拷貝構造函數,但我不認爲這是可能的,因爲不知道涉及的實際類型。我能想到的唯一解決方案是在層次結構中的每種類型上定義clone()方法。任何人都能想到更優雅的東西嗎?

回答

12

不幸的是虛擬克隆/複製圖案是你唯一真正的選擇。

這有些不同,但基本上他們都歸結爲每種類型的函數,可以返回一個新的副本。

2

我不認爲這會很容易做到。事實上,在有效的C++,邁爾斯警告說,如果你有傳址值的函數,並說你有

void foo(Base b) 

,你傳遞一個Derived1 d1foo,副本會被切掉 - 即派生零件不會被複制。

但後來我從記憶的時刻報價...

4

您需要在層次結構中的每個對象上實現虛擬克隆方法。你的未定義類型的對象怎麼知道如何複製自己?

爲了讓編寫派生類的人更加明白這一點,您可以考慮在基類中創建抽象的克隆方法,以強制人們至少寫一些東西。

在我的代碼的幾個地方,我也測試以確保to_string()方法正確實現以將對象序列化爲字符串。我已經在我的課堂上使用,以測試克隆,同時to_string具體的測試看起來是這樣的:

Base *obj1, *obj2; 
# initialize obj1 in a reasonable manner 
obj2 = obj1->clone(); 
assert(obj1->to_string() == obj2->to_string()); 

這是測試兩種克隆方法()和對象序列化到字符串(所以它不是嚴格意義上的單元測試),但是它是一個範例,它可以簡單地包裝在工廠中的所有對象的循環中,以確保它們遵循一些實現clone()和to_string()的最低標準。

編輯:更正代碼,更正來自strager的評論。謝謝!

+0

我想你是指Base * obj1,* obj2 ;. =] – strager 2009-01-27 02:09:36

+0

好抓手! – 2009-02-04 06:07:01

0

複製構造函數是不可能的(據我所知),因爲內存被分配之前構造函數被調用。使用已知類型的複製構造函數,而不是對象的類型。

Base *foo = new Derived1(); 
Base bar = *foo;    // Base's copy constructor is called. 

最好的辦法是按照您的建議提供clone()方法。

-1

另一種方法是創建一個工廠類Base,假設你知道你的所有派生類:

關於第二個想法,這可能是一個壞主意!

class Base { 
    public: 
     static Base *clone(const Base *obj) { 
      if((Derived1 *o = dynamic_cast<Derived1 *>(obj)) { 
       return new Derived1(*o); 
      } else if((Derived2 *o = dynamic_cast<Derived2 *>(obj)) { 
       return new Derived2(*o); 
      } 

      /* TODO When more classes are added, put them here. (Probably bad design, yes.) */ 

      return new Base(obj); 
     } 

     Base *clone() const { 
      return clone(this); 
     } 
}; 
0

你對clone()是正確的。你不能使用構造函數,因爲它們強制靜態綁定。你不能通過值返回一個對象,因爲它再次強制靜態綁定。因此,您需要一個返回動態分配對象的虛擬函數。