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;
}
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;
}
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:
rhs
(tramite il costruttore di copia, chiamato automaticamente)*this
*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:
this
e rhs
contenutirhs
Così passaggio per valore è in questo caso più efficiente di passaggio per riferimento.
In realtà, non importa se MyClass ha un costruttore predefinito. Solo se i membri dati e le classi base hanno uno ... –
sì hai ragione. –
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
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).
Direi che non va bene se MyClass
alloca memoria o è modificabile.
Se non è mutabile, non avrà un 'operatore =' - questa è una funzione di muting. O non intendo la stessa cosa come mutabile come te? –
È 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.
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) {}
};
};
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.
@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.
Ok, ho modificato la mia risposta per fornire maggiori dettagli. –
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. –
@Steve: per 'operator =', dato che devi comunque fare la copia, non può essere peggio. –
E è tecnicamente OK, se si dispone di un operatore di assegnazione di lavoro (operatore di copia).
Tuttavia, si dovrebbe preferisce copia-e-swap, perché:
Utilizzare il [linguaggio copia-e-swap] (http://stackoverflow.com/questions/3279543/what-is-the-copy-and- Swap-linguaggio). – GManNickG
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
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