2012-09-04 13 views
18

Ich suchte herum und scheint, um dies durchzuführen, muss ich meine Basisklasse ändern und möchte wissen, ob dies der beste Ansatz ist. Zum Beispiel Ich habe eine Basisklasse:C++: Tiefes Kopieren eines Base Class Pointer

class Base {} 

Dann eine lange Reihe von abgeleiteten Klassen:

class Derived_1:: public Base {} 
class Derived_2:: public Derived_1{} 
... 
... 
class Derived_n:: public Derived_M{} 

Und dann habe ich eine andere Klasse:

class DeepCopy 
{ 
    Base * basePtr; 

    public: 
    DeepCopy(DeepCopy & dc) {} 
} 

die Basis Unter der Annahme, Klassen- und Derived_x-Klassenkopierkonstruktoren sind korrekt codiert, was ist der beste Weg, den Kopierkonstruktor für DeepCopy zu schreiben. Wie können wir über die Klasse wissen, die sich im basePtr des Objekts befindet, das wir kopieren werden?

Nur so, wie ich denken kann, ist RTTI verwenden, aber eine lange Liste von dynamic_casts Verwendung scheint nicht richtig. Außerdem muss DeepCopy über die Vererbungshierarchie der Basisklasse Bescheid wissen. Die andere Methode, die ich sah, ist here. Aber es erfordert Base und abgeleitete Klassen, eine Klon-Methode zu implementieren.

Also gibt es eine viel einfachere, standardmäßige Möglichkeit, dies zu tun?

+0

Wenn Sie einen POD Datentyp verwendet wurden, würde ich sagen, 'memcpy', aber da Sie sind nicht Sie, könnte Vorlagen verwenden. –

Antwort

24

Sie benötigen die virtuelle Kopie Muster verwenden: eine virtuelle Funktion in der Schnittstelle zur Verfügung stellen, die die Kopie tut und es dann über die Hierarchie implementieren:

struct base { 
    virtual ~base() {}    // Remember to provide a virtual destructor 
    virtual base* clone() const = 0; 
}; 
struct derived : base { 
    virtual derived* clone() const { 
     return new derived(*this); 
    } 
}; 

Dann wird das DeepCopy Objekt muss nur anrufen, dass Funktion:

class DeepCopy 
{ 
    Base * basePtr;  
public: 
    DeepCopy(DeepCopy const & dc)   // This should be `const` 
     : basePtr(dc.basePtr->clone()) 
    {} 
}; 
+0

Danke. Müssen wir das * in der Basisklasse clone() nicht zurückgeben? – madu

+1

@madu Wenn Sie tatsächliche Objekte der Basisklasse haben wollen, sollten Sie 'base :: clone' genauso implementieren wie für die abgeleitete Klasse:' return new base (* this); '.Wenn Sie die Basisklasse nur als Basisklasse verwenden möchten, aber nicht, um tatsächliche Objekte davon zu instanziieren, übergehen Sie am besten ihre Definition, indem Sie 'virtual base * clone() const = 0;'. – jogojapan

+0

@ madu: Richtig, das war ein Fehler in der Antwort. Die virtuelle Memberfunktion sollte entweder rein oder korrekt implementiert sein. –

1

ich denke, dass Vorlagen der beste Weg, in dieser Situation zu gehen:

template<typename Sub> 
class DeepCopy 
{ 
    Base *base; 

    DeepCopy(Sub *sub) 
    { 
     base = new Sub(*sub); // use copy constructor 
    } 
} 

Dies bedeutet, dass DeepCopy nicht zuweisbar sind, aber das ist der Preis, den Sie mit C++ bezahlen.

19

Verwenden Sie einen Ansatz, der eine clone()-Funktion verwendet, ist eine gute Lösung. Hinweis mit der CRTP (the curiously recurring template pattern) können Sie einige der Arbeit speichern. Die Art und Weise, wie Sie es tun, besteht darin, eine Zwischenebene (im Folgenden BaseCRTP genannt) einzuführen, die eine Vorlage ist und die clone()-Funktion implementiert. Wenn Sie Ihre tatsächlichen Klassen ableiten, verwenden Sie sie als Vorlageargument der Basis, von der sie abgeleitet sind. Sie werden die clone() Funktion automatisch für sie implementiert bekommen. Stellen Sie sicher, dass die abgeleiteten Klassen einen Kopierkonstruktor implementieren (oder stellen Sie sicher, dass der Standard Ihren Anforderungen entspricht).

/* Base class includes pure virtual clone function */ 
class Base { 
public: 
    virtual ~Base() {} 
    virtual Base *clone() const = 0; 
}; 

/* Intermediate class that implements CRTP. Use this 
* as a base class for any derived class that you want 
* to have a clone function. 
*/ 
template <typename Derived> 
class BaseCRTP : public Base { 
public: 
    virtual Base *clone() const { 
     return new Derived(static_cast<Derived const&>(*this)); 
    } 
}; 

/* Derive further classes. Each of them must 
* implement a correct copy constructor, because 
* that is used by the clone() function automatically. 
*/ 
class Derived1 : public BaseCRTP<Derived1> { 
    /*... should have an ordinary copy constructor... */ 
}; 

class Derived2 : public BaseCRTP<Derived2> { 
    /*... should have an ordinary copy constructor... */ 
}; 

Sie können dann offensichtlich implementieren die DeepCopy Klasse in gewohnter Weise:

class DeepCopy 
{ 
    Base *basePtr;  
public: 
    DeepCopy(const DeepCopy &dc) 
    : basePtr(dc.basePtr->clone()) 
    {} 
}; 
+0

wusste nicht über CRTP, netter Trick :) –

+1

Plus eins für die Ordentlichkeit von diesem. Das ist der eleganteste Weg, den ich je gesehen habe. – namezero

+0

@jogojapan Elegante +1. Was, wenn wir Derived11 von Derived1 erben müssen? Überschreiben Sie die Klonfunktion mit Derived11 Template-Argument oder gibt es einen richtigen Weg? – ataman