2012-06-27 51 views
1

所有這些看起來都是一個簡單的問題,但它給了我適合。假設我有一個叫做Animal的C++基類,派生類Cat,Dog,Horse,Hippo,Snake。我有另一個名爲Zoo的類,其中包含Animal對象的列表(在這種情況下,基類Animal是QObject的列表)。現在我想要做的是創建一個Zoo對象,並能夠將其複製到另一個Zoo對象,並讓目標Zoo有它自己的動物副本。問題出在Zoo拷貝構造函數中,因爲它認爲它有一個動物列表,因爲編譯器應該這樣做,所以它調用Animal的拷貝構造函數而不是Dog,Cat等。爲了解決這個問題,我將動物列表更改爲動物指針列表,然後創建了我自己的動物副本構造函數和賦值操作符。其中的每一個都調用Animal :: Clone(Animal & rhs)方法,該方法依次調用Animal Clone()方法的每個派生類,該方法返回從堆中分配的新指針及其數據的副本。這一切工作正常,但我一直認爲我錯過了一個更優雅的解決方案。所以我的問題是,當你有一個類型爲object的容器時,我們如何複製包含該容器的類?我希望這是有道理的。繼承元素的容器副本

+2

您是否嘗試過爲'Animal'虛擬製作複製構造函數? – Daniel

+0

我同意丹尼爾重寫父類的函數的唯一方法是讓函數成爲虛函數,否則就是編譯器中生成的代碼。 –

+0

動物從QObject繼承時出現問題。如果我理解正確Qt不允許從QObject繼承的對象的副本。有人知道這是否正確? – Brad

回答

2

你所做的幾乎是在C++中使用它的慣用方法。虛擬clone()方法實際上被稱爲虛擬複製構造函數成語。通常,您會將其聲明爲以下簽名的虛擬抽象方法。

Animal * clone() const = 0; 

注意,它不帶任何參數rhs,它只是返回自身的克隆。鑑於動物Animal * myPet的實例,你可以這樣

Animal * mySecondPet = myPet->clone(); 

你不得不來實現它的派生類,如果他們被實例化得到一個重複的一個。

[編者按:]正如你已經找到了,這是不可能的容器類,以通過指針而不是直接持有的項目,由於C的設計slicing problem固有++。在C++中,你的解決方案是一個習慣用法:它像它的優雅一樣,每個看到它的人,誰知道他們的C++,都應該馬上知道你的意思。成語和某些設計模式是語言的一部分。你不能稱自己熟練掌握一門語言,不管是人類還是編程語言,而不知道習語。

在我的許多項目中,我使用了一個名爲Cloneable的接口類(所有抽象虛擬方法),它僅由此克隆方法組成。然後很容易強制某些基類成爲可複製的:只從Cloneable繼承。例如,我希望那樣QEventCloneable。事實上,複製QEvents以將它們發佈到多個事件隊列是不可能的。

Daniel的建議「您是否嘗試過爲動物虛擬製作複製構造函數?」不能從字面上理解。在C++中沒有虛擬拷貝構造函數或任何構造函數這樣的事情 - 出於一個簡單的原因:在構造對象之前,其虛擬方法表沒有最終確定。具體來說,Animal的構造函數中的代碼將在任何派生類的構造函數被調用以將虛方法表指針交換到派生類中的代碼之前執行。所以,如果你在構造函數中進行了任何虛擬方法調用,他們將不會去任何派生自你的類的類。而已。

+0

這很有道理。然後在Zoo類中實現複製構造函數和賦值運算符,以循環調用每個Animal的clone()方法的QList,對吧? – Brad

+0

這是正確的。 –