2010-03-05 8 views

risposta

10

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.

0

è 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)

+1

Il distruttore è anche automatico (il compilatore non li renderà * virtual *, tuttavia, ma questo è un altro problema). – visitor

5

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).

+0

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

4

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.

1

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)

+0

Trova molto utile l'informazione sul link fornito. – DavidRR

0

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

+1

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

+0

@visitor: vedi la risposta di lilburne - è fondamentalmente la stessa ma più dettagliata nelle sue ragioni - soggettivamente, sento che ha ragione sui soldi. –

+0

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

0

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.

2

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.

+0

dovrebbe 'not' essere' note'? – Bill

+1

"prendi l'abitudine di assicurarti che vengano mantenuti quando cambi la classe". Questo è un inutile incubo di manutenzione. – fredoverflow

+0

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

2

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.

Problemi correlati