2010-02-06 20 views
15

Sono un po 'confuso sulla meccanica del costruttore di copie. Correggimi se ho torto:Posso chiamare esplicitamente un costruttore di copie?

Se un metodo prende un riferimento a un oggetto come parametro e la classe definisce un costruttore di copie, la classe usa il costruttore per creare una copia di se stesso e che viene passata a la funzione invece di un riferimento all'oggetto originale?

Inoltre, si può chiamare

Object * obj = new Object(&anotherObject); 

per creare una copia di anotherObject?

+1

tua terminologia non dovrebbe suggerire che 'anotherObject' sta creando una copia di se stesso .. dell'oggetto che viene creato dopo la nuova sarà la creazione di una copia di' anotherObject'. –

+5

@Hassan: non modificare il significato della domanda senza chiedere all'OP. Questa è una distinzione importante. '& anotherObject' è un puntatore, non un riferimento. –

+0

"non si può dire" che 'new object (& anotherObject)' creerà implicitamente una copia di un altro oggetto (che è ciò che la formulazione suggerisce). Affinché tale affermazione sia valida, la sintassi deve essere corretta. –

risposta

26

No, se una funzione prende un riferimento:

void f1(Object & o); // call by reference 

quindi nessuna copia è fatta. Se una funzione assume un valore:

void f2(Object o); // call by value 

quindi una copia viene creata dal compilatore utilizzando il costruttore di copie.

E sì, quando si dice:

Object * obj = new Object(anotherObject); // not &anotherObject 

il costruttore di copia viene utilizzato in modo esplicito (supponendo anotherObject è di tipo Object.) Non c'è nulla di magico circa l'uso di new qui, tuttavia - in questo caso:

Object obj2(anotherObject); 

viene anche utilizzato il costruttore di copie.

+1

"quindi una copia viene creata dal compilatore usando il costruttore di copie." - Può usare il costruttore di mosse, e anche questo è uno scenario di copia-elisione quindi forse nemmeno nessun costruttore. –

7

Se un metodo prende un riferimento a un oggetto come parametro, il costruttore di copia non verrà chiamato. In tal caso, una chiamata al costruttore di copie avrebbe comportato un loop infinito (poiché prende come riferimento un riferimento).

Questa riga non è un modo valido per chiamare un costruttore di copie. Si aspetta un riferimento come argomento, non un puntatore.

+0

In realtà, l'argomento copy-constructor non è solido. Lo standard del linguaggio consente in alcuni casi la copia opzionale e osserva esplicitamente che l'implementazione deve assicurarsi che non ci sia una ricorsione infinita nel caso di copy-constructor, come un esempio (vedere la nota 93 in C++ 03) – AnT

+0

@AndreyT: Interessante. Ad ogni modo, questo non cambia le cose in questo caso poiché stiamo parlando di * non * copiare in caso di riferimento, che è una cosa piuttosto sicura. –

+0

Bene, la parte dello standard cui mi riferivo è in realtà sull'inizializzazione del riferimento e consente esplicitamente la copia in alcune situazioni (nel caso di inizializzazione di riferimento const). Ovviamente, i compilatori normalmente non si dedicano alla copia ingiustificata, ma sono noti esempi di compilatori che creano strane copie illogiche (ma ancora legali). – AnT

1

No in entrambi i casi. Nel primo caso, il riferimento a quell'oggetto stesso viene passato e la copia non viene creata. Nel secondo caso si passa un puntatore al costruttore di object, quindi non viene creata alcuna copia. Così oggetto deve avere un costruttore (non un costruttore di copia), che è qualcosa di simile object(anotherClass*)

1

copia costruttore viene chiamato solo quando passa per valore, non per riferimento. Per riferimento non è necessaria alcuna copia (questo è parte di ciò che i riferimenti sono per!), Quindi non è stato chiamato alcun costruttore di copie.

3

Il fatto che si stia effettuando una chiamata al metodo non ha importanza qui. L'inizializzazione dei parametri di riferimento durante una chiamata di funzione non è diversa da un'inizializzazione di riferimento autonoma ed è regolata dalle stesse regole.

Le regole di inizializzazione del riferimento sono un po 'complicate, ma la linea di fondo è che se l'inizializzatore è un lvalue (l'argomento nella chiamata al metodo nel tuo caso) e il tipo del riferimento è lo stesso del tipo di l'inizializzatore (cioè il tipo del parametro è uguale al tipo di argomento), quindi il riferimento sarà associato direttamente. Cioè non viene creata alcuna copia.

Object a; // class type 
Object &r = a; // no copying 
const Object &cr = a; // no copying 

Se questi requisiti non sono soddisfatti (come se l'inizializzatore è un rvalue, per esempio), allora tutto dipende. In alcuni casi la copia potrebbe e avverrà. Ad esempio

const Object &tr = Object(); 

può essere interpretato dal compilatore come

const Object &tr = Object(Object(Object(Object()))); 

con numero finito dipende dall'implementazione di copyings. Ovviamente, per motivi di efficienza, i compilatori normalmente cercano di non creare copie non necessarie, anche quando sono autorizzati a copiare.

Un classico esempio che spesso suscita dibattito sulla validità del comportamento copia del compilatore è l'inizializzazione di riferimento nelle espressioni come la seguente

Object a; 
const Object &r = <some condition> ? a : Object(); 

Una persona che conosce la semantica C++ di riferimento capirebbe che espressioni come quanto sopra è probabilmente la logica alla base dell'autorizzazione standard per eseguire copie superflue durante l'inizializzazione del riferimento.

+0

sei sicuro di quella cosa di profondità infinita? C++ 03 8.5.3 dice che ci sono solo due casi: (a) "Il riferimento è legato all'oggetto rappresentato dal valore di rvalue (vedi 3.10) o ad un sottooggetto all'interno di quell'oggetto", e (b) " Viene creato un temporaneo di tipo "cv1 T2" [sic] e viene chiamato un costruttore per copiare l'intero oggetto rvalue nel temporaneo.Il riferimento è legato al temporaneo o ad un sottooggetto all'interno del temporaneo ". Si noti che il caso (b) dice * vincolato a *, non * inizializzato da *, quindi non vedo come tale (b) ne permetta più di un temporaneo aggiuntivo. –

+0

Ovviamente, C++ 14 (che è uscito dopo aver scritto la risposta) consente solo il caso (a), cioè non è possibile creare altri provvisori oltre a 'Object()'. Non ho controllato la formulazione di C++ 11 sebbene l'ISTR avesse un difetto riguardante l'inizializzazione dei riferimenti da rvalues. –

0

sì che utilizzano nuova collocazione in questo modo:

Object dstObject; 
new(&dstObject) Object(&anotherObject); 
Problemi correlati