2009-06-20 11 views
6

Considerare:C++: i costruttori di copia polimorfica potrebbero funzionare?

class A 
{ 
public: 
    A(int val) : m_ValA(val) {} 
    A(const A& rhs) {} 
    int m_ValA; 
}; 

class B : public A 
{ 
public: 
    B(int val4A, int val4B) : A(val4A), m_ValB(val4B) {} 
    B(const B& rhs) : A(rhs), m_ValB(rhs.m_ValB) {} 
    int m_ValB; 
}; 

int main() 
{ 
    A* b1 = new B(1, 2); 
    A* b2 = new A(*b1); // ERROR...but what if it could work? 
    return 0; 
} 

Would C++ essere rotto se "nuovo A (b1)" è stato in grado di risolvere per la creazione di una nuova copia B e la restituzione di un A?

Questo potrebbe essere utile?

risposta

19

Hai bisogno di questa funzionalità o è solo un esperimento mentale?

Se avete bisogno di fare questo, l'idioma comune è quello di avere un metodo Clone:

class A 
{ 
public: 
    A(int val) : m_ValA(val) {} 
    A(const A& rhs) {} 
    virtual A *Clone() = 0; 
    int m_ValA; 
}; 

class B : public A 
{ 
public: 
    B(int val4A, int val4B) : A(val4A), m_ValB(val4B) {} 
    B(const B& rhs) : A(rhs), m_ValB(rhs.m_ValB) {} 
    A *Clone() { return new B(*this); } 
    int m_ValB; 
}; 

int main() 
{ 
    A* b1 = new B(1, 2); 
    A* b2 = b1->Clone(); 
    return 0; 
} 
+5

+1, ma vorrei anche aggiungere un distruttore virtuale alla classe;) –

+0

E' stato davvero un esperimento mentale. E mentre questo è il modo standard per implementare i costruttori di copie virtuali, ero curioso di sapere perché il linguaggio non fornisce un modo standard per farlo. – 0xC0DEFACE

+0

Il ritorno covariante è migliore: B * B :: Clone() –

0

Sì. No.

Esistono diversi modi per implementare la clonazione (ad esempio il metodo clone() più o meno standard, le variazioni parametrizzate delle fabbriche di oggetti, con o senza l'iniezione di dipendenza configurabile) senza modificare il significato dei programmi esistenti o effettuare impossibile creare istanze di classi base quando una classe derivata è nota all'interno di un'unità di compilazione.

Costruttori e distruttori sono sufficientemente complessi da comprendere per i principianti così come sono. Iniettare ancora più complessità in essi sarebbe poco saggio.

1

L'espressione

new A(*b1) 

ha già un significato, supponendo si aveva l'overload appropriato.

Se è stato assegnato un significato diverso, è necessario fornire un altro modo per ottenere l'altro significato. Questa è una specie di inutile dato che c'è già un modo per ottenere il significato che si desidera:

new B(*b1) 

E indovinate che è più chiaro da leggere.

+1

Ma il secondo esempio presuppone che si sappia a priori che 'b1' è in effetti un' B'. – user470379

+0

@ user470379 - assolutamente; il mio punto non era che 'new B (* b1)' è la risposta, era che 'new A (* b1)' non può essere la risposta. –

0

Come sottolineato sopra ci sono diversi modi per implementarlo.

Per rispondere alla tua domanda se new A(*b1) ha restituito una nuova istanza di B, questo non funzionerebbe.

int main() 
{ 
    A* b1 = new B(1, 2); 
    A a(*b1); // What size would 'a' be if it was polymorphicly constructed? 
    return 0; 
} 
2

Basta una piccola aggiunta alla risposta da eduffy: Invece di

class B : public A { 
    ... 
    A *Clone() { return new B(*this); } 
    ... 

};

è possibile dichiarare in questo modo:

class B : public A { 
    ... 
    B *Clone() { return new B(*this); } // note: returning B * 
    ... 

};

E 'ancora considerato un imperativo valida del virtuale A::Clone(); ed è meglio se chiamato direttamente attraverso B *

+0

Fantastico, ma non capisco perché sia ​​valido? ^^ – Thecheeselover

+0

Perché quelli sono tipi di ritorno covarianti. § 10.3.7 –

Problemi correlati