2010-10-23 48 views
2

在簡單的例子繼承層次:在C++中乾淨地複製一個基類或子類的實例?

class Food 
{ 
    virtual ~Food(); 
}; 

class Fruit : public Food 
{ 
    virtual ~Fruit(); 
}; 

class Apple: public Fruit 
{ 
    virtual ~Apple(); 
} 

class Vegetable: public Food 
{ 
    virtual ~Vegetable(); 
} 

我想創建一個可以克隆其子類或基類的實例對象的方法:

Apple* apple1 = new Apple(); 
Apple* clone1 = apple1->clone(); 

Food* food1 = apple1; 
Apple* clone2 = food1->clone(); 

我看到一些可能的解決問題的方案:

  • 使用多態性來創建一個爲每個子類顯式定義的虛函數。

  • 使用模板工廠方法獲取類的實例和子類的類型以創建新實例。

  • 向其他地方存儲的每個類和子類註冊一個id,以查找在調用基類複製函數時要創建哪個子類。

這些都不看起來是理想的,但我更傾向朝着第三解決方案,它可以簡化調用clone函數,而無需爲每個子類中要寫入的定義(這會有很多)。

但是,我非常樂意提供任何建議,有沒有更好的方法來做到這一點?

回答

2

您可以使用CRTP自動實現克隆方法。

template<typename T, typename Derive> class CloneImpl : public Derive { 
public: 
    virtual Derive* clone() { 
     return new T(static_cast<const T&>(*this)); 
    } 
}; 
class Food { 
public: 
    virtual Food* clone() = 0; 
    virtual ~Food() {} 
}; 
class Fruit : public Food { 
}; 
class Dairy : public Food { 
}; 
class Apple : public CloneImpl<Apple, Fruit> { 
}; 
class Banana : public CloneImpl<Banana, Fruit> { 
}; 
class Cheese : public CloneImpl<Cheese, Dairy> { 
}; 
class Milk : public CloneImpl<Milk, Dairy> { 
}; 

在這種情況下,你可以隨時調用clone()當前對象上堆新的分配複製你不需要任何類實現一遍。當然,如果你的克隆語義需要不同,那麼你可以改變這個函數。

CRTP不僅可以爲你實現clone(),它甚至可以在不同的繼承層次之間執行它。

+0

@DeadMG:您的解決方案似乎不適用於OP的類層次結構。你能告訴你如何解決這個層次問題:Food < - Fruit < - Apple,這樣所有的類都可以克隆? – 2010-10-23 14:16:02

+0

@Alf:你錯了,因爲我錯過了Fruit中介課程,所以我會編輯。 – Puppy 2010-10-23 14:20:38

+0

@DeadMG:在OP的例子中,這些(顯然)是具體的類。現在它們是抽象的。需要克隆的層次結構具體實例(在C++ 98中)是一個異常類層次結構。那麼,你們也許可以讓這些課程具體化嗎?這樣所有的課程都是可克隆的?乾杯, – 2010-10-23 14:31:28

-3

你的最後一個例子,...

Food* food1 = dynamic_cast<Food*>(apple1); 
Apple* clone2 = f1->clone(); 

...將無法正常工作,甚至糾正了speling eror。你需要投其他方式:

Food* food1 = apple1; 
Apple* clone2 = dynamic_cast<Apple*>(f1->clone()); 

除此之外,克隆了切實可行的解決方案在C++中定義一個宏:

#define YOURPREFIX_IMPLEMENT_CLONING(Class)      \ 
    virtual Class*             \ 
     virtualCloneThatIsUnsafeToCallDirectly() const    \ 
    {                \ 
     assert(typeid(*this) == typeid(Class));    \ 
     return new Class(*this);         \ 
    }                \ 
                    \ 
    OwnershipPtr<Class>           \ 
     clone() const            \ 
    {                \ 
     return OwnershipPtr<Class>(        \ 
      virtualCloneThatIsUnsafeToCallDirectly()    \ 
      );              \ 
    } 

...其中OwnershipPtr可能是如std::auto_ptr

然後你所要做的就是在每個應該支持克隆的類中放置一個宏調用。簡單。

也可以通過模板實現可重複使用的克隆,但這對實現和使用來說都更復雜。您可以在我的博客中發佈"3 ways to mix in a generic cloning implementation"。但結論是宏觀是最實用的。

+1

不,實際的解決方案絕對不是宏。 – Puppy 2010-10-23 14:04:17

+0

@DeadMG,你有任何支持該觀點的論據嗎?例如。更乾淨和實用的東西?我最感興趣的是整個C++社區。 :-) Cheers&hth。, – 2010-10-23 14:07:19

+0

@DeadMG,PS,如果你無法想出更實用的東西,請刪除downvote。 TIA。, – 2010-10-23 14:09:08

相關問題