2016-06-23 5 views
1

Dieser Code führt zu einem Speicherverlust, weil rA als ungültig initialisiert wird, wenn es erstellt wird. Wann kann ich dieses Problem beheben?Wie verhindere ich den Speicherverlust von unique_ptr

Verwenden Sie shared_ptr oder hoffe auf zukünftige Compiler-Versionen, um diesen schlechten Code zu fangen?

#include <memory> 
using namespace std; 

struct A {}; 
void use(const A& a) {}; 

unique_ptr<A> foo() 
{ 
    unique_ptr<A> pa(new A()); 
    return pa; 
} 

int main() 
{ 
    const A& rA = *foo(); // rA is unusable, initialized with invalid reference (invalidated by destruction of temporary unique_ptr returned from foo) 
    use(rA); 
} 
+0

Was genau ist Ihr Problem? Was meinst du mit "locker"? –

+1

Das temporäre Objekt wird am Ende der Anweisung (Semikolon) zerstört, und Sie haben eine freie Referenz. –

+0

"lose" = "Verlust", vermutlich. –

Antwort

6

Rewrite Ihre main als:

int main() 
{ 
    auto a = foo(); 

    use(*a); 
} 

Als Nebenwirkung würde ich umschreiben foo wie:

std::unique_ptr<A> foo() 
{ 
    return std::make_unique<A>(); 
} 
+0

zerstört. Warum "return std :: move (pa);" es wird zurück zu "Return pa" ausgewertet; –

+3

'return std :: move ...' kann NRVO brechen. –

+0

@ Jonathan Potter, ja du hast Recht, ich habe die Antwort aktualisiert - nicht erkannt, std :: move könnte den Compiler von einem Optimierungsschritt verhindern - danke :-) – keith

2

Wenn Sie Objekte zurück von Wert Sie eine temporäre zurückgeben, wird zerstört werden sofort, es sei denn, es wird von der Seite des Anrufers kopiert oder an eine Variable gebunden.

Was Sie falsch machen, ist das Binden eines Verweises auf etwas, das das zurückgegebene temporäre Objekt enthält und nicht das zurückgegebene Objekt selbst. Zu dem Zeitpunkt, an dem Sie auf das Objekt zugreifen, das Sie gebunden haben, wurde ein Verweis darauf vom Destruktor des temporären Objekts gelöscht, wenn es zerstört wurde.

Um zu zeigen, was man falsch machen ein äquivalentes Beispiel unter Verwendung eines std::vector und Bindung einen Verweis auf eines ihrer Elemente Ich habe geschrieben:

void use(const int& a) {} 

std::vector<int> foo() 
{ 
    return {1, 2, 3}; 
} 

int main() 
{ 
    const int& rA = foo()[0]; // bind to an element 
    // the vector itself is destroyed by the time we get here 

    use(rA); // whoops using a reference to an element from a destroyed vector 
} 
+0

So sollte der Compiler idealerweise sehen, dass dieser Alias ​​ich Einstellung (durch einen Verweis auf den zurückgegebenen Vektor) bedeutet, dass der Vektor noch nicht aus dem Rahmen gehen kann? Idealerweise sollte sich der Compiler zur Kompilierzeit beschweren. – Damian

+0

@Damian Es wäre nett, wenn der Compiler sich beschwert hätte, aber vielleicht ist es nicht so einfach für den Compiler zu wissen, dass Sie etwas falsch machen. Es kann legitimen Code geben, der so aussieht und der Compiler kann nicht zwischen ihnen unterscheiden. Wie weiß der Compiler schließlich, dass das Objekt, an das Sie binden, zerstört wird, wenn der Vektor zerstört wird? – Galik

+0

Ich denke, es würde wissen, ob ich direkt über diesen Vektor auf das Ergebnis bezogen v = foo() auf diese Weise ist es immer noch im Anwendungsbereich. Aber ich bin nicht sicher, wie der Compiler sich beschweren würde, wenn wir nur einen Verweis/Alias ​​nehmen. – Damian