2014-07-24 64 views
8

我在C++中定義了兩個類。那些是基類,一個是派生類如何在C++中作爲派生對象克隆

class CBaseClass 
    { 
    … 
    } 

    class CDerivedClass : public CBaseClass 
    { 
    … 
    } 

而想要實現clone功能如下:

CBaseClass *Clone(const CBaseClass *pObject) 
    { 
    } 

當CDerivedClass的對象傳遞給克隆,則該函數將還創建一個CDerivedClass對象並返回。 當一個CBaseClass對象被傳遞給Clone時,該函數也會創建一個CBaseClass對象並返回。

如何實現這樣的功能?

+4

怎麼樣虛成員函數'clone'? –

+2

'Clone'應該是由你的每個層次實現的虛擬成員方法,包括'CDerivedClass' – quantdev

+0

[如何在C++中克隆對象?或者是否有另一種解決方案?](http://stackoverflow.com/questions/12902751/how-to-clone-object-in-c-or-is-there-another-solution) – quantdev

回答

0

可以與虛擬克隆方法做到這一點,和一個輔助模板CRTP類來實現此接口:

class CBaseClass { 
    //... 
    virtual CBaseClass * Clone() = 0; 
    std::unique_ptr<CBaseClass> UniqueClone() { 
     return std::unique_ptr<CBaseClass>(Clone()); 
    } 
    virtual std::shared_ptr<CBaseClass> SharedClone() = 0; 
}; 

template <typename DERIVED> 
class CBaseClassCRTP : public CBaseClass 
{ 
    CBaseClass * Clone() { 
     return new DERIVED(*static_cast<DERIVED *>(this)); 
    } 
    std::shared_ptr<CBaseClass> SharedClone() { 
     return std::make_shared<CbaseClass>(*static_cast<DERIVED *>(this)); 
    } 
}; 

class CDerivedClass : public CBaseClassCRTP<CDerivedClass> 
{ 
    //... 
}; 

現在,每一個派生類GETE助手類的Clone方法禮貌。

+1

如果你有一個基地會發生什麼指針並不知道確切的類型?你應該返回'DERIVED *' –

+0

'dynamic_cast'是不必要的,你應該在這裏使用一個智能指針。 – Puppy

+0

將待克隆對象傳遞給克隆函數似乎很奇怪。我習慣於像這樣調用克隆:'cloned_object = some_object.clone()'。 –

1

這是一個簡單的解決方案。請記住爲繼承中的每個類提供一個克隆。

class Base 
{ 
public: 
    virtual ~Base() {} 
    virtual Base *Clone() const 
    { 
     // code to copy stuff here 
     return new Base(*this); 
    } 
}; 

class Derived : public Base 
{ 
public: 
    virtual Derived *Clone() const 
    { 
     // code to copy stuff here 
     return new Derived(*this); 
    } 
}; 
+0

很好地使用協變返回類型,但可能需要一些額外的解釋。 –

+1

沒有CRTP?原始指針返回? Downvote。 – Puppy

+2

@Puppy Covariant返回無法與unique_ptr一起工作,所以我放棄了。請告知您是否有更好的解決方案。 –

1

虛擬克隆模式通常用於解決這些問題。經典解決方案傾向於對clone()方法使用協變式返回類型。其他解決方案在基類和派生類之間注入工廠類型類(使用CRTP)。甚至有解決方案只是用宏來實現這個功能。請參閱C++ FAQ,C++ idiomsblog on this。這些解決方案中的任何一個都是可行的,最合適的將取決於它們被使用和準備使用的環境。

一個經典的方法,使用covariant return types,加上更現代的RAII技術(shared_ptr等)提供了一個非常靈活和安全的組合。協變返回類型的一個優點是,您可以在參數的層次結構中的同一級別上獲得一個克隆(即返回並不總是基類)。

該解決方案確實需要訪問shared_ptr和/或unique_ptr。如果您的編譯器不可用,boost會提供這些選項。 clone_sharedclone_unique模擬相應的make_sharedmake_unique實用程序形成標準庫。它們包含對參數和目標類型的類層次結構的顯式類型檢查。

#include <type_traits> 
#include <utility> 
#include <memory> 

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

class CDerivedClass : public CBaseClass { 
public: 
    virtual CDerivedClass * clone() const { 
    return new CDerivedClass(*this); 
    } 
}; 

class CMoreDerivedClass : public CDerivedClass { 
public: 
    virtual CMoreDerivedClass * clone() const { 
    return new CMoreDerivedClass(*this); 
    } 
}; 

class CAnotherDerivedClass : public CBaseClass { 
public: 
    virtual CAnotherDerivedClass * clone() const { 
    return new CAnotherDerivedClass(*this); 
    } 
}; 

// Clone factories 

template <typename Class, typename T> 
std::unique_ptr<Class> clone_unique(T&& source) 
{ 
    static_assert(std::is_base_of<Class, typename std::decay<decltype(*source)>::type>::value, 
    "can only clone for pointers to the target type (or base thereof)"); 
    return std::unique_ptr<Class>(source->clone()); 
} 

template <typename Class, typename T> 
std::shared_ptr<Class> clone_shared(T&& source) 
{ 
    static_assert(std::is_base_of<Class, typename std::decay<decltype(*source)>::type>::value, 
    "can only clone for pointers to the target type (or base thereof)"); 
    return std::shared_ptr<Class>(source->clone()); 
} 

int main() 
{ 
    std::unique_ptr<CDerivedClass> mdc(new CMoreDerivedClass()); // = std::make_unique<CMoreDerivedClass>(); 
    std::shared_ptr<CDerivedClass> cloned1 = clone_shared<CDerivedClass>(mdc); 
    std::unique_ptr<CBaseClass> cloned2 = clone_unique<CBaseClass>(mdc); 
    const std::unique_ptr<CBaseClass> cloned3 = clone_unique<CBaseClass>(mdc); 
    // these all generate compiler errors 
    //std::unique_ptr<CAnotherDerivedClass> cloned4 = clone_unique<CAnotherDerivedClass>(mdc); 
    //std::unique_ptr<CDerivedClass> cloned5 = clone_unique<CBaseClass>(mdc); 
    //auto cloned6 = clone_unique<CMoreDerivedClass>(mdc); 
} 

我添加了一個CMoreDerivedClassCAnotherDerivedClass擴大層次一點點,以便更好地展示各類檢查等

Sample code

+0

當克隆共享指針時,您將需要調用'make_shared'來避免內存分配開銷。 – jxh

+0

@jxh,好點。 'make_shared'雖然創建了對象本身,這也是'clone'方法的功能;因爲'clone_shared'方法實際上不知道它的參數的派生類型是什麼(僅僅是目標參數之間的關係),所以產生衝突。我認爲它仍然有可能,但我覺得它超出了問題的範圍。我認爲'clone_unique'幾乎總是更有意義,但代碼不限於此。 – Niall

+0

CRTP提供了派生類的知識。我同意這兩種方法都不能提供完美的解決方案。 – jxh