Darò una risposta in più dalla prospettiva di Modern C++ di "evitare puntatori grezzi se è possibile". Ma sarò anche sottolineare una distinzione importante che si dovrebbe essere a conoscenza di:
C++ constructor syntax
Ma in primo luogo, prendiamo in considerazione ciò che la vostra intenzione è. Se ho scritto:
Sample x = 1;
Sample y = x;
Quale dovrebbe essere la semantica?
Qualora i Sample
"copie" hanno ciascuno il proprio 'ptr' indipendenti, la cui punta-di opporsi vita dura solo fino a quando la classe cui vivono?
In genere, non è necessario alcun puntatore se questo è ciò che si desidera.
maggior parte del tempo, la dimensione totale della classe sarà abbastanza ragionevole che l'allocazione di stack non sarà un problema se state dichiarando senza new
(come tu sei qui). Quindi perché coinvolgere i puntatori? Basta usare int i
(o qualunque sia la tua classe non POD).
Se effettivamente avere il tipo di caso in cui si fai necessità di allocare dinamicamente grandi blocchi di dati da gestire se stessi (vs. rimandando a C++ collezioni delle biblioteche o simili), quelli potrebbero exceed your stack. Se hai bisogno di allocare dinamicamente, avrai bisogno di copiare la costruzione in un modo o nell'altro. Ciò significa che Sample
dovrà gestire in modo esplicito la costruzione della copia -oppure- utilizzare una classe smart pointer che la impone in modo che lo non sia necessario a.
Prima diciamo si sta tenendo il puntatore grezzo, che vorrebbe dire:
Sample(const Sample & other)
{
ptr = new int(*other.ptr);
}
MA si potrebbe ridurre il rischio di errori in questa situazione usando un unique_ptr
invece. Un unique_ptr distruggerà i dati puntati dal puntatore raw che tiene automaticamente quando viene eseguito il suo distruttore. Quindi non devi preoccuparti di chiamare delete
.
Anche, un unique_ptr si rifiuterà di copiare per impostazione predefinita. Quindi se hai appena scritto:
class Sample
{
public:
unique_ptr<int> ptr;
Sample(int i)
{
ptr = std::unique_ptr<int>(new int(i));
}
~Sample()
{
cout << "destroyed";
}
void PrintVal()
{
cout << "The value is " << *ptr;
}
};
La classe stessa può essere costruita, ma si otterrebbero degli errori nei callites. Faranno notare che stai facendo delle copie per qualcosa per cui la costruzione della copia non è stata definita correttamente. Non solo quello...non si sta solo facendo una copia nel vostro programma, ma due:
In function ‘int main()’:
error: use of deleted function ‘Sample::Sample(const Sample&)’
Sample s1 = 10;
^
note: ‘Sample::Sample(const Sample&)’ is implicitly deleted
because the default definition would be ill-formed:
error: use of deleted function ‘Sample::Sample(const Sample&)’
SomeFunc(s1);
^
che ti dà un testa a testa per aggiungere un costruttore di copia equivalente a:
Sample(const Sample & other)
{
ptr = std::unique_ptr<int>(new int(*other.ptr));
}
Più probabilmente voler cambiare Sample s1 = 10;
a Sample s1 (10);
per evitare la copia lì. Del resto, potresti aver voluto che SomeFunc
prendesse i suoi valori anche come riferimento. Menzionerò anche lo initializer lists vs assignments.
(Nota:. C'è in realtà un nome per il modello di una classe puntatore intelligente che copia chiamato clone_ptr
, in modo da non dover scrivere anche che costruttore di copia Non è nella libreria standard C++, ma you'll find implementations around.)
Qualora i Sample
"copie" condividere una dinamica ptr
comune che viene eliminato solo dopo l'ultimo riferimento va via?
Più facile con puntatori intelligenti e nessun costruttore di copia necessario sul campione. Utilizzare un shared_ptr
. Il comportamento predefinito di shared_ptr deve poter essere copiato con assegnazioni semplici.
class Sample
{
public:
shared_ptr<int> ptr;
Sample(int i)
{
ptr = make_shared<int>(i);
}
~Sample()
{
cout << "destroyed";
}
void PrintVal()
{
cout << "The value is " << *ptr;
}
};
Morale della favola è che più si può lasciare che i comportamenti predefiniti fare il lavoro giusto per te ... e il codice si scrive meno ... meno potenziale avete per i bug. Quindi, mentre è bello sapere cosa fanno i costruttori di copie e quando vengono richiamati, può essere altrettanto importante sapere come scrivere non!
Nota che unique_ptr e shared_ptr provengono da C++ 11 e richiedono #include <memory>
.
Sottolinei cosa passa per mezzo di riferimento? – doctorlove
buon esempio del perché i costruttori di copia sono utili –
@Claptrap Piuttosto un buon esempio del perché non si dovrebbe gestire la memoria da soli e lasciare che un puntatore intelligente lo faccia. Meglio della regola di tre/cinque è la [** regola di zero **] (http://flamingdangerzone.com/cxx11/2012/08/15/rule-of-zero.html) – JBL