2014-07-24 8 views
8

Ich definiere zwei Klassen in C++. Ones ist die Basisklasse, und man ist eine abgeleitete KlasseWie klon als abgeleitetes Objekt in C++

class CBaseClass 
    { 
    … 
    } 

    class CDerivedClass : public CBaseClass 
    { 
    … 
    } 

und will eine Klon-Funktion implementieren, wie folgt:

CBaseClass *Clone(const CBaseClass *pObject) 
    { 
    } 

Wenn ein Objekt von CDerivedClass geben wird zu klonen, dann wird die Funktion Erstellen Sie außerdem ein CDerivedClass-Objekt, und geben Sie zurück. Wenn ein Objekt von CBaseClass an Clone übergeben wird, erstellt die Funktion auch ein CBaseClass-Objekt und return.

Wie eine solche Funktion zu implementieren?

+4

Wie über eine virtuelle Memberfunktion 'clone' zu ​​erweitern? –

+2

'Clone' sollte eine virtuelle Member-Methode sein, die von jeder Klasse Ihrer Hierarchie implementiert wird, einschließlich' CDerivedClass' – quantdev

+0

mögliches Duplikat von [Wie klonst du Objekte in C++? Oder gibt es eine andere Lösung?] (Http://stackoverflow.com/questions/12902751/how-to-clone-object-in-c-or-is-there-another-solution) – quantdev

Antwort

0

Sie können dies mit einer virtuellen Clone-Methode erreichen, und eine Vorlage CRTP Klasse Helfern diese Schnittstelle zu implementieren:

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> 
{ 
    //... 
}; 

nun jede abgeleitete Klasse gete eine Clone Methode mit freundlicher Genehmigung der Hilfsklasse.

+1

Was passiert, wenn Sie eine Basis haben Zeiger und kenne den genauen Typ nicht? Sie sollten 'DERIVED *' –

+0

'dynamic_cast' zurückgeben. Sie sollten hier einen Smart Pointer verwenden. – Puppy

+0

Es scheint seltsam, das zu klonende Objekt in die Klonfunktion zu übergeben. Ich bin es gewohnt, das Klonen wie folgt aufzurufen: 'cloned_object = some_object.clone()'. –

1

Hier ist eine einfache Lösung. Denken Sie daran, für jede Klasse in der Vererbung einen Klon anzugeben.

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

Nice Verwendung von kovarianten Rückgabetyp, aber das erfordert wahrscheinlich einige zusätzliche Erklärung. –

+1

Kein CRTP? Rohe Zeigerrückgabe? Downvote. – Puppy

+2

@Puppy Kovariante Rückkehr funktionierte nicht mit unique_ptr, also gab ich auf. Bitte geben Sie an, wenn Sie eine bessere Lösung haben. –

1

Das virtuelle Klonmuster wird häufig verwendet, um Probleme wie diese zu lösen. Klassische Lösungen neigen dazu, für die Methode clone() co-variante Rückgabetypen zu verwenden. Andere Lösung injizieren eine Factory-Typ-Klasse (mit CRTP) zwischen der Basis und den abgeleiteten Klassen. Es gibt sogar Lösungen, die diese Funktionalität nur mit Makros implementieren. Siehe die C++ FAQ, C++ idioms und eine blog on this. Jede dieser Lösungen ist praktikabel, und die geeignetste hängt vom Kontext ab, in dem sie verwendet werden und verwendet werden sollen.

Ein klassischer Ansatz, mit covariant return types und gekoppelt mit moderneren RAII-Techniken (shared_ptr et al.) Bietet eine sehr flexible und sichere Kombination. Einer der Vorteile des kovarianten Rückgabetyps besteht darin, dass Sie in der Lage sind, einen Klon auf derselben Ebene in der Hierarchie wie das Argument zu erhalten (d. H. Die Rückgabe erfolgt nicht immer zur Basisklasse).

Die Lösung erfordert Zugriff auf shared_ptr und/oder unique_ptr. Falls Booster mit Ihrem Compiler nicht verfügbar ist, bietet Boost Alternativen für diese. Die clone_shared und clone_unique sind den entsprechenden make_shared und make_unique Dienstprogrammen der Standardbibliothek nachgebildet. Sie enthalten explizite Typprüfungen für die Klassenhierarchie der Argumente und Zieltypen.

#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); 
} 

Ich habe hinzugefügt, eine CMoreDerivedClass und CAnotherDerivedClass die Hierarchie ein wenig besser zu zeigen Typen Kontrollen usw.

Sample code

+0

Sie sollten 'make_shared' aufrufen, um bei der Cloning von geteilten Zeigern einen Speicherzuordnungsaufwand zu vermeiden. – jxh

+0

@jxh, guter Punkt. 'make_shared' erzeugt zwar das Objekt selbst, was die' clone' Methode auch macht; daher ein Konflikt, weil die 'clone_shared'-Methode nicht wirklich weiß, was der am meisten abgeleitete Typ ihres Arguments ist (nur die Beziehung zwischen dem Argument des Ziels). Ich denke, dass es immer noch möglich sein könnte, aber ich fühlte es über den Rahmen der Frage hinaus.Ich denke, der 'clone_unique' würde fast immer mehr Sinn machen, aber der Code ist nicht darauf beschränkt. – Niall

+0

Die Kenntnis der abgeleiteten Klasse ist der Vorteil, den CRTP bietet. Kein Ansatz bietet eine perfekte Lösung, stimme ich zu. – jxh