Meine Frage kann auf Folgendes reduziert werden: Woher kommt die Zeichenfolge, die von stringstream.str().c_str()
live im Speicher zurückgegeben wurde, und warum kann sie nicht einer zugewiesen werden const char*
?
Dieses Codebeispiel erklärt es besser als ich
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
int main()
{
stringstream ss("this is a string\n");
string str(ss.str());
const char* cstr1 = str.c_str();
const char* cstr2 = ss.str().c_str();
cout << cstr1 // Prints correctly
<< cstr2; // ERROR, prints out garbage
system("PAUSE");
return 0;
}
Die Annahme, stringstream.str().c_str()
die einem zugeordnet werden konnte, const char*
führte zu einem Fehler, bei dem ich eine Weile brauchte, um ihn aufzuspüren.
Kann jemand für Bonuspunkte erklären, warum er die cout
Erklärung durch ersetzt
cout << cstr // Prints correctly
<< ss.str().c_str() // Prints correctly
<< cstr2; // Prints correctly (???)
druckt die Zeichenfolgen richtig?
Ich kompiliere in Visual Studio 2008.
quelle
str()
so implementiert ist, dass RVO einschalten kann (was sehr wahrscheinlich ist), kann der Compiler das Ergebnis direkt erstellen intmp
, die vorübergehende eliding; und jeder moderne C ++ - Compiler wird dies tun, wenn Optimierungen aktiviert sind. Natürlich garantiert die Bind-to-Const-Referenzlösung keine Kopie, daher ist sie möglicherweise vorzuziehen - aber ich dachte, es lohnt sich immer noch zu klären.Was Sie tun, ist eine temporäre zu erstellen. Diese temporäre Datei ist in einem vom Compiler festgelegten Bereich vorhanden, sodass sie lang genug ist, um die Anforderungen zu erfüllen.
Sobald die Anweisung
const char* cstr2 = ss.str().c_str();
vollständig ist, sieht der Compiler keinen Grund, die temporäre Zeichenfolgeconst char *
beizubehalten , und sie wird zerstört, sodass Sie auf freien Speicher verweisen.Ihre Anweisung
string str(ss.str());
bedeutet, dass die temporäre Datei im Konstruktor für diestring
Variable verwendet wirdstr
, die Sie auf den lokalen Stapel gelegt haben, und die so lange erhalten bleibt, wie Sie es erwarten: bis zum Ende des Blocks oder der von Ihnen geschriebenen Funktion. Daher ist dasconst char *
Innere immer noch ein gutes Gedächtnis, wenn Sie das versuchencout
.quelle
In dieser Zeile:
ss.str()
erstellt eine Kopie des Inhalts des Stringstreams. Wenn Siec_str()
in derselben Zeile anrufen , verweisen Sie auf legitime Daten. Nach dieser Zeile wird die Zeichenfolge jedoch zerstört, sodass Siechar*
auf nicht besessenen Speicher verweisen können.quelle
Das von ss.str () zurückgegebene std :: string-Objekt ist ein temporäres Objekt, dessen Lebensdauer auf den Ausdruck beschränkt ist. Sie können also keinem temporären Objekt einen Zeiger zuweisen, ohne Papierkorb zu erhalten.
Nun gibt es eine Ausnahme: Wenn Sie eine konstante Referenz verwenden, um das temporäre Objekt abzurufen, ist es legal, es für eine längere Lebensdauer zu verwenden. Zum Beispiel sollten Sie tun:
Auf diese Weise erhalten Sie die Zeichenfolge für eine längere Zeit.
Jetzt müssen Sie wissen, dass es eine Art Optimierung namens RVO gibt, die besagt, dass wenn der Compiler eine Initialisierung über einen Funktionsaufruf sieht und diese Funktion eine temporäre zurückgibt, er nicht kopiert, sondern nur den zugewiesenen Wert als temporär festlegt . Auf diese Weise müssen Sie keine Referenz verwenden. Nur wenn Sie sicher sein möchten, dass sie nicht kopiert wird, ist dies erforderlich. Also:
wäre besser und einfacher.
quelle
Das
ss.str()
temporäre wird zerstört, nachdem die Initialisierungcstr2
abgeschlossen ist. Wenn Sie es also mit druckencout
, ist die C-Zeichenfolge, die mit diesemstd::string
temporären Zeichen verknüpft war, schon lange nicht mehr vorhanden. Sie haben also Glück, wenn es abstürzt und behauptet, und nicht Glück, wenn es Müll druckt oder scheinbar funktioniert.Die C-Zeichenfolge, auf die
cstr1
verweist, ist jedoch einer Zeichenfolge zugeordnet, die zum Zeitpunkt der Ausführung noch vorhanden ist,cout
sodass das Ergebnis korrekt gedruckt wird.Im folgenden Code ist der erste
cstr
korrekt (ich nehme an, er befindet sichcstr1
im realen Code?). Die zweite druckt die C-Zeichenfolge, die dem temporären Zeichenfolgenobjekt zugeordnet istss.str()
. Das Objekt wird am Ende der Auswertung des vollständigen Ausdrucks, in dem es erscheint, zerstört. Der vollständige Ausdruck ist der gesamtecout << ...
Ausdruck. Während die Ausgabe des C-Strings erfolgt, ist das zugehörige String-Objekt weiterhin vorhanden. Denncstr2
- es ist reine Schlechtigkeit, dass es gelingt. Möglicherweise wählt es intern denselben Speicherort für das neue temporäre Element aus, das es bereits für das zum Initialisieren verwendete temporäre Element ausgewählt hatcstr2
. Es könnte auch abstürzen.Die Rückgabe von
c_str()
wird normalerweise nur auf den internen Zeichenfolgenpuffer verweisen - dies ist jedoch keine Voraussetzung. Die Zeichenfolge könnte einen Puffer bilden, wenn ihre interne Implementierung beispielsweise nicht zusammenhängend ist (das ist gut möglich - aber im nächsten C ++ - Standard müssen Zeichenfolgen zusammenhängend gespeichert werden).In GCC verwenden Zeichenfolgen Referenzzählung und Copy-on-Write. Sie werden also feststellen, dass das Folgende zutrifft (zumindest bei meiner GCC-Version).
Die beiden Zeichenfolgen teilen sich hier den gleichen Puffer. Wenn Sie einen von ihnen ändern, wird der Puffer kopiert und jeder hat seine eigene Kopie. Andere String-Implementierungen machen die Dinge jedoch anders.
quelle