2012-10-18 6 views
7

ich habe eine ziemlich grundlegende C++ Frage, eine Funktion betrachten, die einige Eingabeparameter nimmt und schafft eine std::string, dass aus diesen Parametern wie unten:C++ Rückkehr temporäre Objekte Verwirrung

std::string constructString(int some_parameter) { 

    std::stringstream ss; 

    // Construct a string (arbitrarily complex) 
    ss << "Some parameter is " << some_parameter << " right now"; 

    return ss.str(); //Am I not returning a temporary object here? 
} 

Ich verstehe, dass die stringstream-object wird den Gültigkeitsbereich verlassen, wenn die Funktion zurückkehrt, aber macht das nicht auch den konstruierten String ungültig?

Was würde passieren, wenn ich den Rückgabetyp auf const char * änderte und stattdessen ss.str().c_str() zurückgab?

Code wie oben scheint zu funktionieren, aber ich vermute, dass nur weil der Speicher mit dem 'temporären' Objekt noch nicht mit etwas anderem überschrieben wurde, wenn ich es verwende?

Ich muss zugeben, ich bin in solchen Situationen im Allgemeinen ziemlich verwirrt, ich würde es schätzen, wenn jemand mir diese ganze "temporäre Objekte" erklären könnte (oder mir einfach in die richtige Richtung zeigen).

thx im Voraus

Antwort

10

Sie sind ein temporäres Objekt zurückkehrt, sondern weil Sie es als Wert zurückgeben, wird die Kopie erstellt. Wenn Sie einen Zeiger oder eine Referenz auf ein temporäres Objekt zurückgeben, wäre das ein Fehler.

Wenn Sie den Rückgabetyp const char * und ss.str().c_str() zurückkehren ändern würden Sie Zeiger zu einem gewissen Puffer von temporären std::string zurück Rückkehr von ss.str() und das wäre schlecht.

2

Wie Sie sehen Stringstream::str() gibt std::string Objekt zurück. Sie geben std::string ohne Referenz zurück, das bedeutet, dass ohne RVO (NRVO) -Optimierung Kopie Konstruktor wird aufrufen und erstellen std::string Objekt gültig. Mit Optimierung wird std::string ohne Kopierkonstruktor verschoben. Aber wenn std::string& zurückkommt, wird es abstürzen, weil dieses Objekt nach Funktionsrückgabe zerstört wird. Der gleiche Effekt wird mit const char * sein, weil nach der Zerstörung dieser Zeiger auf schlechten Speicher zeigen wird, und das ist gefährliche Situation.

+0

Wird der Destruktor aufgerufen, nachdem diese Kopie nicht von der Funktion aufgerufen wird, die constructString aufruft? Da es sich um ein temporäres Objekt handelt, nehme ich an, dass der Zeiger auf den Stapel als Rückgabewert gehalten wird. Was passiert nach der Rückgabe? Wird es zerstört, wenn kein neuer Objektbesitzer vorhanden ist? Wie der Compiler weiß, dass das Objekt zerstört werden muss oder nicht, nachdem es zurückgegeben wurde? –

1

Nehmen wir an: T val = some_function(), wenn Sie einen Wert von some_function C++ kopieren Wert des zurückgegebenen Werts in val mit angegebenen Kopie Konstruktor oder integrierten Operator. Also, wenn Sie eine int oder std::string zurückgeben, gibt es überhaupt kein Problem, aber wenn Sie einen Zeiger auf einen Speicher zurückgeben, der am Ende der Funktion freigegeben wird, oops !! Ihr Zeiger wird auf einen ungültigen Speicher verwiesen. Zum Beispiel betrachten dies:

const char* some_function() { 
    std::string res(...); 
    //... 
    return res.c_str(); 
} 

Sie Zeiger auf die Daten zurückkommen, die sobald Funktion Rückkehr befreit werden (da res zerstört werden und es wird seine internen Daten frei), so finden Sie die Adresse, aber das Adresse zeigt nicht auf das, was Sie möglicherweise erwarten!

+0

* "C++ kopiere den Wert des zurückgegebenen Wertes in val" * - Sogar noch mehr als das, es kopiert bereits den Wert von 'ss.str() 'in den Rückgabewert der Funktion, weil er als Wert zurückgibt. Also würde es sogar mit 'const T & val = some_function()' funktionieren, wenn 'some_function' als Wert zurückgegeben wird. –

+1

'const T & val = some_function()' funktioniert eigentlich perfekt, da C++ jedes Ding nach Wert kopiert und 'const T &' ist eigentlich 'const T *' mit einer anderen Semantik, das Problem hier ist 'const T &' zeigt auf einen Ort das wird nach dem Funktionsaufruf für ungültig erklärt! – BigBoss

+1

Nein, wird es nicht, da das Binden einer Const-Referenz an ein Temporäres die Lebensdauer des Temporären verlängert. Deshalb 'void foo (const std :: string &); foo ("test"); "funktioniert einwandfrei. Es funktioniert jedoch nicht für einen Zeiger (oder eine nicht konstante Referenz). Dies ist einer der kleinen Unterschiede, die wirklich etwas anderes als syntaktischen Zucker für Zeiger machen. –