2012-09-04 25 views
18

Ho cercato in giro e sembra che per eseguire questo ho bisogno di cambiare la mia classe Base e voglio sapere se questo è l'approccio migliore. Per esempio, ho una classe base:C++: copia profonda di un puntatore della classe Base

class Base {} 

Poi una lunga serie di classi derivate:

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

E poi ho un'altra classe:

class DeepCopy 
{ 
    Base * basePtr; 

    public: 
    DeepCopy(DeepCopy & dc) {} 
} 

Supponendo che la Base i costruttori di copia classe e Derived_x sono opportunamente codificati, qual è il modo migliore per scrivere il costruttore di copia per DeepCopy. Come possiamo sapere della classe che si trova nella basePtr dell'oggetto che stiamo per copiare?

L'unico modo in cui posso pensare è utilizzare RTTI, ma l'utilizzo di un lungo elenco di dynamic_cast non sembra corretto. Inoltre richiede a DeepCopy di conoscere la gerarchia di ereditarietà della classe Base.

L'altro metodo che ho visto è here. Ma richiede che le classi Base e Derivate implementino un metodo di clonazione.

Quindi c'è un modo molto più semplice e standard per farlo?

+0

Se si sta utilizzando un tipo di dati POD, direi 'memcpy', ma dal momento che non siete, voi potrebbe usare i modelli. –

risposta

24

È necessario utilizzare il virtuale copia modello: fornire una funzione virtuale nell'interfaccia che fa la copia e poi attuarlo attraverso la gerarchia:

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

Poi l'oggetto DeepCopy ha solo bisogno di chiamare quel funzione:

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

Grazie. Non abbiamo bisogno di restituire questo * nella classe base clone()? – madu

+1

@madu Se si desidera avere oggetti reali della classe base, è necessario implementare 'base :: clone' nello stesso modo della classe derivata:' return new base (* this); '.Se si desidera utilizzare la classe base solo come classe base, ma non per creare un'istanza di oggetti reali, saltare la sua definizione impostando 'virtual base * clone() const = 0;'. – jogojapan

+0

@madu: Giusto, quello era un bug nella risposta. La funzione membro virtuale dovrebbe essere pura o correttamente implementata. –

1

credo che i modelli sono il modo migliore per andare in questa situazione:

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

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

Ciò significa che gli DeepCopy non sono assegnabili tra loro, ma questo è il prezzo che si paga con C++.

19

Utilizzare un approccio che utilizza una funzione clone() è una buona soluzione. Nota utilizzando CRTP (the curiously recurring template pattern) puoi risparmiare parte del lavoro. Il modo in cui lo fai è introducendo un livello intermedio (chiamato BaseCRTP in basso) che è un modello e implementa la funzione clone(). Quando si derivano le classi effettive, utilizzarle come argomento modello della base da cui derivano. Otterranno automaticamente la funzione clone() implementata per loro. Assicurati che le classi derivate implementino un costruttore di copie (o assicurati che l'impostazione predefinita sia ciò di cui hai bisogno).

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

È possibile poi, ovviamente, implementare la classe DeepCopy nel solito modo:

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

non sapeva di crtp, bel trucco :) –

+1

Più uno per la pulizia di questo. Questo è il modo più elegante che abbia mai visto. – namezero

+0

@jogojapan Elegante +1. Cosa succede se abbiamo bisogno di ereditare Derived11 da Derived1? Sovrascrivi la funzione clone con argomento template Derived11 o esiste un modo corretto? – ataman

Problemi correlati