2013-10-16 14 views
6

Ich weiß, dass es ähnliche Fragen zu diesem aber keiner von ihnen eine endgültige Antwort auf meine Frage geben ...C++ Rückkehr Verweis auf Neues Objekt

Sind beide diese in Bezug auf die besten Praktiken in Ordnung? Oder sollte ich einen Zeiger zurückgeben? Und wenn nicht wie sollten sie geändert werden, um Best Practices zu folgen.

Ich möchte einen Verweis auf ein neues Objekt aus einer Funktion zurückgeben. Meine Implementierung ist wie folgt:

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

MyClass a = doSomething(); 

Ist das in Ordnung, weil eine neue Instanz von MyClass ist mit neuen auf dem Heap zugewiesen werden?

Oder sollte ich es konstant machen (ich bin nicht wirklich sicher, wann ich das mache oder nicht)?

Und wenn beide falsch sind, sollte ich nur einen Zeiger auf das neue Objekt zurückgeben

Danke.

+0

Kein const notwendig, erste Option ist gut. Sie können auch einen Zeiger zurückgeben, wenn Sie möchten. Wie du willst. –

+1

Aber die Referenz wurde vom Anrufer nicht zugewiesen. Es wurde während des Aufrufs instanziiert ... also dachte ich mir, wenn du nicht neu verwendest, würde es den Gültigkeitsbereich verlassen, weil es auf dem Stapel statt auf dem Haufen wäre. –

+4

@BrianCain Er braucht unbedingt 'new', weil sonst das Objekt zerstört wird, sobald' doSomething() 'beendet ist. –

Antwort

8

Das ist zwar nicht gerade falsch, aber keine gute Idee.

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

Nach Ihrer Rückkehr niemand hat den ursprünglichen Zeiger, so wird niemand jemals delete es. * Es ist also ein Speicherleck.

Sie sollten eigentlich nie eine new schreiben, es sei denn, Sie haben eine entsprechende delete -oder, besser, ein intelligenter Zeiger Konstruktor.


Inzwischen diese Zeile in Ihrem ursprünglichen Code:

MyClass a = doSomething(); 

... wird eine Kopie des Wertes ohnehin machen. Angenommen, es handelt sich nicht um einen weiteren Fehler, der behoben werden muss, warum sollten Sie ein Objekt heap-allokieren und einen Verweis zum Kopieren und Lecken zurückgeben? Einfach das ausge Objekt nach Wert:

MyClass doSomething() { 
    return MyClass(); 
} 

Jetzt müssen Sie sich nicht über das Löschen nichts kümmern, weil Sie nie etwas auf dem Heap erstellt.


Best Practices können in der Regel in den vier Buchstaben RAII zusammengefasst werden: Ressourcen Acquisition Initialisierung ist. (Und die logische Folge, dass Zerstörung ist Release.) Wenn Sie etwas haben, das unmöglich oder teuer ist, um Wert zu übergeben, dann übergeben Sie einen Griff nach Wert. Zum Beispiel:

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

unique_ptr<MyClass> a = doSomething(); 

Jetzt ist es nur ein Zeiger, der herum kopiert wird. Das Objekt selbst wird innerhalb von doSomething erstellt und gelöscht, sobald a den Gültigkeitsbereich verlässt (oder, wenn Sie es an eine andere Variable übergeben, immer wenn , dass den Geltungsbereich verlässt, usw.).

Auf der anderen Seite, wenn MyClass ist nur eine Handvoll leicht kopierbar Werte **, kopieren Sie es einfach.


* Es ist nicht unmöglich jemals löschen; Sie können immer einen Zeiger auf die Referenz und delete das nehmen. Es ist sehr unwahrscheinlich, dass Sie das jemals tun werden, und es wird peinlich aussehen. Wenn Sie Zeiger weitergeben möchten, übergeben Sie Zeiger. Wenn Sie keine Zeiger weitergeben möchten, müssen Sie Besitz in einer Klasse einschließen und die Klasse nach Wert übergeben.

** Mit "leicht kopierbar" meine ich, dass es einfach ist, sie sicher zu kopieren, und dass Sie tatsächlich so tun. Ein roher Pointer oder ein Datei-Handle sind zum Beispiel nur ein paar Bytes, und der Standard-Copy-Konstruktor kopiert sie gerne für Sie ... aber dann haben Sie mehrere Verweise auf dasselbe Heap-Objekt oder dieselbe Heap-Datei, und es ist unmöglich, sie zu verfolgen wer ist verantwortlich für das Löschen oder Schließen.

+0

Geht letzterer nicht von RVO aus? Oder habe ich etwas vermisst ... (tue ich oft, also überrasche wenn es so ist). – WhozCraig

+0

@WhozCraig: Mit oder ohne RVO kann der Compiler den neu erstellten Zeiger nicht optimieren, weil Sie explizit danach gefragt haben. Wenn Sie nach Wert anstatt nach Referenz zurückgeben, dann kann RVO eine Kopie optimieren, und die ganze Sache wird im Wesentlichen frei sein. – abarnert

+0

Ich müsste die Klasse außerhalb der Funktion erstellen, um sie nach Wert korrekt zu übergeben? Danke für die Speicherleck-Informationen Ich dachte, dass etwas Seltsames vor sich ging, aber ich konnte nicht recht herausfinden, was es war. Grundsätzlich ist das, was ich erschaffe, eine Art Fabrikmethode. Also sollte ein Zeiger wieder funktionieren? –

Verwandte Themen