2013-10-16 3 views
6

So che ci sono domande simili a questo, ma nessuno di loro dare una risposta definitiva alla mia domanda ...C++ Tornando riferimento al nuovo oggetto

Sono entrambi questi va bene in termini di best practice? O dovrei restituire un puntatore? E se non come dovrebbero essere cambiati per seguire le migliori pratiche.

Desidero restituire un riferimento a un nuovo oggetto da una funzione. La mia implementazione è la seguente:

MyClass& doSomething() { 
    return *(new MyClass()); 
} 

MyClass a = doSomething(); 

È questo bene, perché una nuova istanza di MyClass viene allocato sul heap con nuova?

O dovrei renderlo costante (non sono davvero sicuro quando farlo o meno)?

const MyClass& doSomething() { 
    return *(new MyClass()); 
} 

E se entrambi questi sono errati dovrei semplicemente restituire un puntatore al nuovo oggetto?

Grazie.

+0

No const necessario, la prima opzione è buona. Inoltre, puoi anche restituire un puntatore se lo desideri. Sta a te. –

+1

Ma il riferimento non è stato assegnato dal chiamante. È stato istanziato durante la chiamata ... ho pensato che se non avessi usato di nuovo, sarebbe andato fuori portata perché sarebbe stato in pila piuttosto che in un heap. –

+4

@BrianCain Ha assolutamente bisogno di 'new', perché altrimenti l'oggetto verrà distrutto non appena termina' doSomething() '. –

risposta

8

Sebbene ciò non sia esattamente non valido, non è una buona idea.

MyClass& doSomething() { 
    return *(new MyClass()); 
} 

Al rientro, nessuno ha il puntatore originale, in modo che nessuno potrà mai delete esso. * Quindi è una perdita di memoria.

si dovrebbe praticamente mai scrivere un new se non si ha un corrispondente delete -o, meglio, un costruttore puntatore intelligente.


Nel frattempo, questa riga nel codice originale: ... sta andando

MyClass a = doSomething(); 

di fare una copia del valore in ogni caso. Supponendo che non fosse un altro bug da correggere, perché preoccuparsi di allocare un heap all'oggetto e restituire un riferimento a copia e perdita? Basta restituire l'oggetto per valore:

MyClass doSomething() { 
    return MyClass(); 
} 

Ora non c'è bisogno di preoccuparsi di cancellare nulla, perché non hai mai creato nulla sul mucchio.


Le best practice possono in genere essere riassunte nelle quattro lettere RAII: Acquisizione risorsa è inizializzazione. (E il corollario, quella distruzione è il rilascio.) Se hai qualcosa che è impossibile, o costoso, da passare in giro per valore, allora passa attorno ad un handle per valore. Ad esempio:

unique_ptr<MyClass> doSomething() { 
    return unique_ptr<MyClass>(new myClass()); 
} 

unique_ptr<MyClass> a = doSomething(); 

Ora è solo un puntatore che viene copiato. L'oggetto stesso viene creato all'interno di doSomething e cancellato ogni volta che a esce dallo scope (o, se lo si passa ad un'altra variabile, ogni volta che che esce dal campo di applicazione, ecc.).

D'altra parte, se MyClass è solo una manciata di valori facilmente copiabili **, basta copiarlo.


* Non è impossibile per cancellare mai esso; puoi sempre prendere un puntatore al riferimento e delete. È molto improbabile che lo farai mai, e sembrerà imbarazzante. Se vuoi passare i puntatori, passa i puntatori. Se non si desidera passare i puntatori in giro, avvolgere la proprietà in una classe e passare la classe in base al valore.

** Con "facilmente copiabili" intendo che è facile copiarli in modo sicuro e che lo si fa effettivamente. Ad esempio, un puntatore raw o un handle di file sono solo pochi byte, e il costruttore di copie predefinito li copierà volentieri per te ... ma poi finirai con più riferimenti allo stesso oggetto o file heap, ed è impossibile tenere traccia di chi ha il compito di cancellarlo o chiuderlo.

+0

È quest'ultimo che non assume RVO? O mi sono perso qualcosa ... (lo faccio spesso, quindi è una sorpresa se è così). – WhozCraig

+0

@WhozCraig: con o senza RVO, il compilatore non è in grado di ottimizzare il puntatore appena creato, perché è stato esplicitamente richiesto. Se si restituisce per valore invece che per riferimento, _then_ RVO può ottimizzare una copia e l'intera operazione sarà essenzialmente gratuita. – abarnert

+0

Dovrei creare la classe al di fuori della funzione per passarla intorno in base al valore corretto? Grazie per le informazioni sulla perdita di memoria ho pensato che stesse succedendo qualcosa di strano ma non riuscivo a capire cosa fosse. Fondamentalmente quello che sto creando è una sorta di metodo di fabbrica. Quindi restituire un puntatore dovrebbe funzionare allora? –