2010-09-06 14 views
18

Se il operator= è definito correttamente, è corretto utilizzare il seguente come costruttore di copia?Implementazione del costruttore di copie in termini di operatore =

MyClass::MyClass(MyClass const &_copy) 
{ 
    *this = _copy; 
} 
+9

Utilizzare il [linguaggio copia-e-swap] (http://stackoverflow.com/questions/3279543/what-is-the-copy-and- Swap-linguaggio). – GManNickG

+0

In genere, l'operatore di assegnazione copia eseguirà una pulizia. Se la tua classe ha un puntatore alla memoria allocata dinamicamente, la prima cosa che l'operatore di assegnazione della copia dovrebbe fare è liberare quella memoria. Questa implementazione del costruttore di copie darebbe all'operatore di assegnazione della copia un puntatore pendente, che non si desidera eliminare. – Kevin

+0

Anche se si usano puntatori intelligenti (nel qual caso l'eliminazione non rappresenta un problema), si farebbe comunque inutilmente la costruzione e l'assegnazione predefinite di tutte le variabili membro. Basta usare copia e scambio. – Kevin

risposta

27

Se tutti i membri di MyClass hanno un costruttore predefinito, sì.

Si noti che di solito è il contrario:

class MyClass 
{ 
public: 
    MyClass(MyClass const&);  // Implemented 
    void swap(MyClass&) throw(); // Implemented 
    MyClass& operator=(MyClass rhs) { rhs.swap(*this); return *this; } 
}; 

passiamo per valore in operator= in modo che il costruttore di copia viene chiamato. Si noti che tutto è eccezionalmente sicuro, dal momento che lo swap è garantito non da lanciare (è necessario assicurarlo nell'implementazione).

EDIT, come richiesto, la roba call-by-value: Il operator= potrebbe essere scritto come

MyClass& MyClass::operator=(MyClass const& rhs) 
{ 
    MyClass tmp(rhs); 
    tmp.swap(*this); 
    return *this; 
} 

studenti C++ di solito sono detto di passare istanze di classe per riferimento, perché il costruttore di copia viene chiamato se sono passati per valore. Nel nostro caso, dobbiamo comunque copiare rhs, quindi passare per valore va bene.

Così, il operator= (prima versione, chiamata per valore) si legge:

  • Eseguire una copia di rhs (tramite il costruttore di copia, chiamato automaticamente)
  • Scambia il suo contenuto con *this
  • Return *this e lasciare che rhs (che contiene il vecchio valore) venga distrutto all'uscita del metodo.

Ora, abbiamo un bonus extra con questo call-by-value. Se l'oggetto che viene passato a operator= (o qualsiasi funzione che ottiene i suoi argomenti in base al valore) è un oggetto temporaneo, il compilatore può (e di solito lo fa) non fare alcuna copia. Questo è chiamato copia elision.

Pertanto, se rhs è temporaneo, non viene eseguita alcuna copia. Ci siamo lasciati con:

  • Swap this e rhs contenuti
  • Destroy rhs

Così passaggio per valore è in questo caso più efficiente di passaggio per riferimento.

+1

In realtà, non importa se MyClass ha un costruttore predefinito. Solo se i membri dati e le classi base hanno uno ... –

+0

sì hai ragione. –

+0

Ok, grazie. Lo stavo facendo per evitare la duplicazione del codice nell'implementazione di 'operator =' e del costruttore di copie. Con l'idioma copy-and-swap il codice è duplicato nel costruttore di copie e nel metodo 'swap'. Ho ragione? – gregseth

5

Questa implementazione implica che i costruttori predefiniti per tutti i membri dati (e classi base) siano disponibili e accessibili da MyClass, poiché verranno chiamati prima, prima di eseguire l'assegnazione. Anche in questo caso, avere questo richiamo extra per i costruttori potrebbe essere costoso (a seconda del contenuto della classe).

Continuerò comunque a implementare separatamente il costruttore di copie tramite l'elenco di inizializzazione, anche se ciò significa scrivere più codice.

Un'altra cosa: questa implementazione potrebbe avere effetti collaterali (ad es. Se i membri sono allocati dinamicamente).

0

Direi che non va bene se MyClass alloca memoria o è modificabile.

+3

Se non è mutabile, non avrà un 'operatore =' - questa è una funzione di muting. O non intendo la stessa cosa come mutabile come te? –

11

È consigliabile implementare operator = in termini di un costruttore di copia eccezionalmente sicuro. Vedi Esempio 4. in questo Herb Sutter per una spiegazione della tecnica e perché è una buona idea.

http://www.gotw.ca/gotw/059.htm

1

Mentre il risultato finale è lo stesso, i membri sono prima predefinito inizializzata, solo copiato dopo.

Con membri "costosi", è meglio copiare-costruire con un elenco di inizializzazione.

struct C { 
    ExpensiveType member; 

    C(const C& other): member(other.member) {} 
}; 



}; 
+0

Non è possibile inizializzare la costruzione esterna. – GManNickG

+0

@GMan: dannazione. Volevo scrivere il costruttore della copia, non il compito. Perdonami per quello. – xtofl

+0

Nessun problema, rimosso -1. :) – GManNickG

0

sì.

personalmente, se la propria classe non ha puntatori sebbene non sovraccarichi l'operatore uguale o scriva il costruttore di copie e lasci che il compilatore lo faccia per te; implementerà una copia superficiale e saprai per certo che tutti i dati dei membri vengono copiati, mentre se sovraccarichi il = op; e quindi aggiungi un membro dati e poi dimentica di aggiornare il sovraccarico e avrai un problema.

0

@Alexandre - Non sono sicuro di passare per valore nell'operatore di assegnazione. Qual è il vantaggio che otterresti chiamando il costruttore di copie lì? Sta andando ad allacciare l'operatore incaricato?

P.S. Non so come scrivere commenti. O forse non sono autorizzato a scrivere commenti.

+0

Ok, ho modificato la mia risposta per fornire maggiori dettagli. –

+0

Il riferimento abituale è http://cpp-next.com/archive/2009/08/want-speed-pass-by-value/. Non sono ancora convinto che le conclusioni siano sempre corrette. –

+0

@Steve: per 'operator =', dato che devi comunque fare la copia, non può essere peggio. –

0

E è tecnicamente OK, se si dispone di un operatore di assegnazione di lavoro (operatore di copia).

Tuttavia, si dovrebbe preferisce copia-e-swap, perché:

  • sicurezza rispetto alle eccezioni è più facile con copia-swap
  • separazione più logica delle preoccupazioni:
    • La copia-ctor è circa allocando le risorse di cui ha bisogno (per copiare le altre cose).
    • La funzione swap è (soprattutto) solo circa lo scambio di "maniglie" interne e non ha bisogno di fare delle risorse (de) allocazione
    • Il distruttore è di circa deallocazione risorsa
    • copia-e-swap combina naturalmente questi tre funzione dell'operatore di assegnazione/copia
Problemi correlati