2012-09-04 14 views
18

Szukałem i wydaje mi się, że muszę to zrobić. Muszę zmienić swoją klasę bazową i chcę wiedzieć, czy to najlepsze podejście. Na przykład Mam klasy Base:C++: Głębokie kopiowanie wskaźnika klasy podstawowej

class Base {} 

Potem długa kolejka klas pochodnych:

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

A potem mam inną klasę:

class DeepCopy 
{ 
    Base * basePtr; 

    public: 
    DeepCopy(DeepCopy & dc) {} 
} 

Zakładając Bazę konstrukcje klasy class i Derived_x są poprawnie zakodowane, jaki jest najlepszy sposób napisania konstruktora kopiowania dla DeepCopy. Skąd mamy wiedzieć o klasie, która znajduje się w basePtr obiektu, który będziemy kopiować?

Jedyny sposób, jaki mogę wymyślić, to korzystanie z RTTI, ale używanie długiej listy dynamic_casts wydaje się nie w porządku. Poza tym wymaga DeepCopy, aby wiedzieć o hierarchii dziedziczenia klasy Base.

Inną metodą, którą zobaczyłem jest here. Ale wymaga to klas bazowych i pochodnych implementacji metody klonowania.

Czy istnieje o wiele łatwiejszy, standardowy sposób robienia tego?

+0

Jeśli uzywasz typu POD danych, powiedziałbym 'memcpy', ale ponieważ nie są, może używać szablonów. –

Odpowiedz

24

Musisz użyć wirtualnego kopiowania wzór: podać funkcję wirtualnego w interfejsie, dokłada kopię, a następnie wdrożyć go w poprzek hierarchii:

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

Następnie obiekt DeepCopy prostu potrzebuje zadzwonić że funkcja:

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

Dziękuję. Czy nie musimy zwracać tego * w kliencie klasy podstawowej()? – madu

+1

@madu Jeśli chcesz mieć rzeczywiste obiekty klasy bazowej, powinieneś zaimplementować 'base :: clone' w taki sam sposób jak dla klasy pochodnej:' return new base (* this); '.Jeśli chcesz używać klasy bazowej tylko jako klasy bazowej, ale nie do tworzenia instancji jej rzeczywistych obiektów, najlepiej pomiń jej definicję, ustawiając 'virtual base * clone() const = 0;'. – jogojapan

+0

@madu: Tak, to był błąd w odpowiedzi. Funkcja wirtualnego elementu powinna być czysta lub poprawnie zaimplementowana. –

1

myślę, że szablony są najlepszym sposobem, aby przejść w tej sytuacji:

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

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

Oznacza to, że DeepCopy są nieprzypisane sobie nawzajem, ale to cena, którą płacisz w C++.

19

Stosowanie podejścia wykorzystującego funkcję clone() jest dobrym rozwiązaniem. Uwaga: użycie CRTP (the curiously recurring template pattern) może zaoszczędzić trochę pracy. Sposób, w jaki to robisz, polega na wprowadzeniu poziomu pośredniego (o nazwie BaseCRTP poniżej), który jest szablonem i implementuje funkcję clone(). Kiedy wyprowadzacie swoje rzeczywiste klasy, używajcie ich jako argumentu szablonu podstawy, z której pochodzą. Dostaną dla nich automatycznie funkcję clone(). Upewnij się, że klasy pochodne implementują konstruktor kopii (lub upewnij się, że domyślne jest to, czego potrzebujesz).

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

Można to oczywiście zaimplementować klasę DeepCopy w zwykły sposób:

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

nie wiedziałem o crtp, miłym triku :) –

+1

Plus jeden za schludność tego. Jest to najbardziej elegancki sposób, jaki kiedykolwiek widziałem. – namezero

+0

@jogojapan Elegancki +1. Co jeśli musimy dziedziczyć Derived11 z Derived1? Zastąpić funkcję klonowania argumentem szablonu Derived11 lub czy istnieje właściwa metoda? – ataman

Powiązane problemy