2016-05-30 6 views
5

Il valore intrinseco di un modello copia-swap per la operator = in un contenitore di dati C++ è detto derivare davalidità del linguaggio copia-swap

  1. Codice riutilizzo e
  2. sicurezza eccezioni.

Tuttavia il linguaggio copia-swap alloca memoria molto più di quanto sarebbe altrimenti necessario, come dati non viene distrutta prima della creazione della copia, aumentando la probabilità di eccezioni sostanzialmente. Dato che questo è il caso, qual è il punto nel sostenere che è un modello utile?

Ci sono qualsiasi altri casi in cui un'operazione di copia potrebbe generare (che non si riferisce agli oggetti copiati) oltre che esaurire la memoria?

+0

Hai appena risposto alla tua prima domanda? I punti nel sostenere che è un modello utile sono: 1. Riutilizzo del codice e 2. Sicurezza dell'eccezione. – immibis

+1

La parola chiave è "trade-off". Vuoi una forte garanzia? Paga il prezzo. Vuoi pagare di meno? Accetta una garanzia più debole. –

+0

* "i dati non vengono distrutti prima della creazione della copia" * Forse ti sto fraintendendo, ma se distruggi i dati per primi, non sarai in grado di copiarli, vero? – Mario

risposta

6

La copia & idioma di scambio per l'operatore di assegnazione restituisce la garanzia di eccezione forte: il valore originale non viene modificato durante la creazione di un nuovo oggetto. Quando si riutilizza la memoria allocata per l'oggetto assegnato, la garanzia di eccezione forte può essere raggiunta in genere solo quando nessuna delle operazioni coinvolte può essere lanciata. Soprattutto quando sono coinvolti modelli di classe (come in std::vector<T>), in genere non vi è alcuna garanzia che nessuna delle operazioni venga lanciata (ovviamente, std::vector<T> fornisce solo la garanzia di base sull'assegnazione).

In generale, gli incarichi di copia possono essere lanciati per qualsiasi motivo. Molto probabilmente non dovrebbero gettare per altri motivi che i fallimenti nell'assegnare le risorse ma non c'è nulla nel concetto CopyAssignable che vieta l'operazione dal lancio.

Ho trovato abbastanza comune che le assegnazioni di copia non sono state implementate correttamente e, ad esempio, violano la garanzia di eccezione di base. L'indicatore che questo è il caso è quando l'operatore incaricato dell'assegnazione ha bisogno di verificare l'autoassegnazione: se è effettivamente necessaria un'assegnazione (piuttosto che essere lì, a volte definita come "ottimizzazione" - quanto spesso le persone fanno incarichi ...?), il codice è quasi certamente sbagliato, molto probabilmente non mantenendo la garanzia di base di eccezione. Di conseguenza, ti consiglio di utilizzare l'approccio di copia & a meno che non ci sia un buon motivo per non farlo anche se sono consapevole che usa più memoria (anche se raramente il problema è che non c'è abbastanza memoria, almeno non sui sistemi Sto lavorando, ma piuttosto che l'uso di più memoria potrebbe causare un'esecuzione più lenta).

+0

Cosa succede se una classe ha un buffer allocato con 'new []'? Copiare un oggetto richiederebbe la duplicazione del buffer, ma il buffer originale nell'oggetto copiato dovrebbe essere 'delete'd. Non vedo come fare questo lavoro senza un assegno per l'autoassegnazione, ma la garanzia debole può essere mantenuta con un po 'di attenzione. Certo, è possibile nasconderlo dietro qualche altra classe contenitore (es. 'Std :: vector'), ma non sono ancora d'accordo con l'affermazione che" se un autoassegnazione è effettivamente necessaria ..., il codice è quasi certamente sbagliato, molto probabilmente non mantenendo la garanzia di base di eccezione. " –

+0

@JoshuaGreen: se si desidera conservare il buffer allocato si è praticamente in un mondo in cui può essere fornita solo la garanzia di base di eccezione. Il modo ovvio per assegnare una sequenza di oggetti è assegnarli individualmente.Nota che non è necessario un assegno di autoassegnazione: il contenitore fa semplicemente affidamento sull'assegnazione di un oggetto costitutivo! Ho visto alcuni casi in cui era necessario il controllo di autoassegnazione e l'operatore di assegnazione era ancora corretto. Tuttavia, si tratta di una minoranza irrilevante: in genere un controllo di autoassegnazione necessario è indicativo di un errore. –

+0

Il caso a cui sto pensando è quando ho voluto eliminare il (vecchio) buffer allocato - per rilasciare qualsiasi memoria nel caso in cui quello copiato sia più piccolo - e quindi riallocarlo e copiare gli elementi. È vero che la riallocazione potrebbe essere salvata se non fossi preoccupato di restituire la memoria non necessaria. –