Dire che ho una classe in cui l'unico membro dati è qualcosa come std::string
o std::vector
. Devo fornire un Copy Constructor, Destructor and Assignment Operator?In quali circostanze devo fornire, operatore di assegnazione, copia costruttore e distruttore per la mia classe C++?
risposta
Nel caso in cui la classe contenga solo oggetti vettore/stringa come membri dei dati, non è necessario implementarli. Le classi C++ STL (come il vettore, la stringa) hanno il proprio codificatore di copia, l'operatore di assegnazione sovraccaricata e il distruttore.
Ma nel caso in cui la classe allochi dinamicamente la memoria nel costruttore, una copia ingenua superficiale causerà dei problemi. In tal caso dovrai implementare il copy ctor, l'operatore di assegnazione sovraccarico e il distruttore.
è necessario fornire loro se ne avete bisogno. o possibili utenti delle tue classi. il distruttore è sempre un deve e copia i costruttori e l'operatore di assegnazione vengono creati automaticamente dal compilatore. (MSVC almeno)
La solita regola pratica dice: se ne hai bisogno, ne hai bisogno.
Non tutte le classi hanno bisogno di loro, però. Se la tua classe non ha risorse (memoria, in particolare), starai bene senza di loro. Ad esempio, una classe con un singolo componente string
o vector
non ne ha realmente bisogno, a meno che non sia necessario un comportamento di copia speciale (l'impostazione predefinita verrà semplicemente copiata sui membri).
Invece di dire "non tutte le classi ne hanno bisogno", non sarebbe più corretto dire che "mantenere il costruttore di copie, il distruttore e l'assegnatore di copie predefiniti andrà bene."? (Cioè, non avrai bisogno di sovrascrivere i valori predefiniti con le tue implementazioni.) – DavidRR
Il costruttore di copia predefinito copia il vettore se è dichiarato per valore. Attenzione se hai memorizzato dei puntatori nel tuo vettore, in tal caso, devi fornire un comportamento specifico per copia/assegnazione/distruzione per evitare perdite di memoria o cancellazione multipla.
questi contenitori necessitano di un elemento "copiabile" e se non si fornisce il costruttore di copie, chiamerà il costruttore di copie predefinito della classe deducendo dai membri della classe (copia superficiale).
facile spiegazione circa costruttore di copia di default è qui: http://www.fredosaurus.com/notes-cpp/oop-condestructors/copyconstructors.html
è così con distruttore, il contenitore bisogno di avere accesso al vostro distruttore o il vostro distruttore di classe di default se non si fornisce uno (vale a dire che lo farà. non funziona se dichiari il tuo distruttore come privato)
Trova molto utile l'informazione sul link fornito. – DavidRR
Quando si dispone di una classe che richiede copie approfondite, è necessario definirli.
In particolare, ogni classe che contiene i puntatori o riferimenti dovrebbe contenere le quali:
class foo {
private:
int a,b;
bar *c;
}
Soggettivamente, direi sempre li definiscono, come il comportamento predefinito fornito dalla versione del compilatore generato non può essere quello che aspetta/vuoi
Potrebbe essere meglio dire: se la classe * possiede * la risorsa. Così com'è, quell'istanza 'bar' che' c' sta puntando a potrebbe essere posseduta e controllata altrove, e 'pippo' è solo un utente che condivide l'oggetto. - Interessante, consiglierei anche * non * di definirli se il default è OK: è molto più probabile che tu commetta errori rispetto al compilatore e interrompi la copia e l'assegnazione (e nel distruttore non c'è niente da fare per te in primo luogo in tal caso). – visitor
@visitor: vedi la risposta di lilburne - è fondamentalmente la stessa ma più dettagliata nelle sue ragioni - soggettivamente, sento che ha ragione sui soldi. –
Ovviamente ne hai bisogno se vuoi qualcosa oltre la copia superficiale, membro. Ma non sono del tutto convinto del perché dovresti farlo manualmente per la copia dei membri (che è la maggior parte delle classi per me, se devono essere copiabili in primo luogo) - se non è quello che ti aspetti, forse tu aspettarsi che la semantica molto bizzarra venga copiata. - Forse un motivo oggettivo per scrivere manualmente l'assegnazione dell'operatore è così che tu possa dare maggiori garanzie di eccezione (lhv non cambiata, non solo nessuna perdita di memoria), ma suppongo che sarebbe molto complicato (la necessità di eseguire il rollback delle modifiche) per essere fatto universalmente. – visitor
Non per stringhe o vettori, poiché i banali costruttori/distruttori ecc. Funzioneranno correttamente.
Se la classe ha indicazioni su altri dati e necessita di copie approfondite o se la classe contiene una risorsa che deve essere deallata o che deve essere copiata in modo speciale.
No, ma ci sono una serie di motivi per cui non si dovrebbe consentire al compilatore di generare automaticamente queste funzioni.
Nella mia esperienza è sempre meglio definirli da soli e prendere l'abitudine di assicurarsi che vengano mantenuti quando si cambia classe. Innanzitutto potresti voler inserire un punto di interruzione quando viene chiamato un determinato ctor o dtor. Inoltre, non definirli può causare un aumento di codice in quanto il compilatore genererà chiamate in linea al membro ctor e dtor (Scott Meyers ha una sezione su questo).
Inoltre, a volte si desidera disabilitare i correttori e i compiti di copia predefiniti. Ad esempio, ho un'applicazione che memorizza e manipola blocchi di dati molto grandi. Abbiamo regolarmente l'equivalente di un vettore STL contenente milioni di punti 3D e sarebbe un disastro se permettessimo che quei contenitori fossero copiati. Quindi gli operatori di ctor e assegnazione sono dichiarati privati e non definiti. In questo modo se qualcuno scrive
class myClass {
void doSomething(const bigDataContainer data); // not should be passed by reference
}
quindi otterrà un errore del compilatore. La nostra esperienza è che un metodo esplicito() o clone() è molto meno soggetto a errori.
Quindi tutto sommato ci sono molte ragioni per evitare le funzioni del compilatore generate automaticamente.
dovrebbe 'not' essere' note'? – Bill
"prendi l'abitudine di assicurarti che vengano mantenuti quando cambi la classe". Questo è un inutile incubo di manutenzione. – fredoverflow
Se non si dispone di test unitari per i propri medici ecc. Per verificare la corretta inizializzazione? Non dovresti considerare tutte le implicazioni dell'aggiunta di membri dei dati alle classi? Se aggiungi una nuova stringa a una classe, qual è l'impatto sul codice gonfiato in tutti i metodi che lo usano e in tutte le classi che potrebbero contenere istanze di esso? Dopo aver aggiunto un nuovo membro non è necessario riconsiderare se autorizzare l'autogenerazione è ancora più fattibile? Mentre ti stai chiedendo tutte queste cose aggiungendo al copy-ctor e op = è minimo. – lilburne
Posso pensare ad alcuni casi in cui è necessario scrivere il proprio Big Three. Tutti i contenitori standard sanno come copiare e distruggere se stessi, quindi non è necessario necessariamente scriverli. Ecco come sapere quando lo fai:
La mia classe possiede risorse?
La semantica di copia predefinite per i puntatori è quello di copiare il valoredel puntatore, non quello a cui punta. Se è necessario copiare in profondità qualcosa, anche se è memorizzato in un contenitore standard, è necessario scrivere il proprio costruttore di copia e l'operatore di assegnazione. È inoltre necessario scrivere il proprio distruttore per liberare correttamente tali risorse.
Qualcuno potrebbe ereditare dalla mia classe?
Le classi base richiedono un distruttore. Herb Sutter consiglia di renderli sia public
e virtual
(caso più comune) o protected
e non virtuali, a seconda di cosa si vuole fare con loro. Il distruttore generato dal compilatore è pubblico e non virtuale, quindi dovrai scrivere il tuo, anche se non contiene alcun codice. (Nota: questo non implica che devi scrivere un costruttore di copia o di assegnazione dell'operatore.)
Devo impedire a un utente di copiare oggetti della mia classe?
Se non si desidera che l'utente di copiare gli oggetti (forse è troppo costoso), è necessario dichiarare la gli operatori costruttore di copia e di assegnazione sia protected
o private
. Non devi implementarli a meno che tu non ne abbia bisogno. (Nota: questo non implica che tu debba scrivere un distruttore.)
Bottom line:
La cosa più importante è capire ciò che il costruttore di copia generato dal compilatore, operatore di assegnamento, e distruttore faranno. Non devi aver paura di loro, ma devi pensare a loro e decidere se il loro comportamento è appropriato per la tua classe.
- 1. Costruttore di copia "quasi predefinito" (& operatore di assegnazione) in C++
- 2. Condizioni in cui il compilatore non definirà impliciti (costruttore, distruttore, costruttore di copia, assegnazione copia)
- 3. operatore di assegnazione sovraccarico in C++
- 4. Operatore di assegnazione C++ nella classe derivata
- 5. Copia costruttore o = operatore?
- 6. enable_if con operatore di assegnazione copia/spostamento
- 7. Il modo più conciso per disabilitare la classe di copia in C++ 11
- 8. predefinito mossa costruttore di default contro costruttore di copia di default vs. operatore di assegnamento
- 9. Copia costruttore di template classe
- 10. ES6 Distruttore in classe costruttore
- 11. Quando dovrei definire il mio operatore di copia e assegnatore di copie
- 12. C++ operatore predefinito di assegnazione
- 13. Operatore di assegnazione del sovraccarico in C++
- 14. razionale dietro C++ copia implicita e spostare costruttore?
- 15. Come posso fornire un supporto personalizzato per la mia classe?
- 16. Operatore di assegnazione di sovraccarico C++
- 17. copia e assegnazione
- 18. Come disabilitare la generazione del costruttore di copie implicitamente definita quando esiste un distruttore definito dall'utente
- 19. problema di visibilità per la copia costruttore della classe base
- 20. C++: cast operatore vs. operatore di assegnazione contro priorità costruttore conversione
- 21. Dovremmo chiamare i costruttori di copia/assegnazione di movimento classe base dalla classe derivata
- 22. ARM C++ ABI: valori di ritorno costruttore/distruttore
- 23. Java comportamento operatore di assegnazione vs C++
- 24. C# generatore costruttore copia
- 25. Operatore di assegnazione con membro della classe di riferimento
- 26. Eccezione e costruttore di copia: C++
- 27. C++: Posso rendere un operatore di assegnazione "esplicito"
- 28. Capire la precedenza di assegnazione e operatore logico in Ruby
- 29. Quali versioni di .NET Framework e C# devo indirizzare con la mia libreria di classi?
- 30. Bad inizializzazione serie vs assegnazione costruttore di copia di inizializzazione
Il distruttore è anche automatico (il compilatore non li renderà * virtual *, tuttavia, ma questo è un altro problema). – visitor