Ma question peut être résumée à, où la chaîne retournée par stringstream.str().c_str()
Live in Memory, et pourquoi ne peut-elle pas être attribuée à un const char*
?
Cet exemple de code l'expliquera mieux que moi
#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;
}
L'hypothèse qui stringstream.str().c_str()
pourrait être attribuée à un a const char*
conduit à un bug qui m'a pris du temps à dépister.
Pour les points bonus, quelqu'un peut-il expliquer pourquoi le remplacement de la cout
déclaration par
cout << cstr // Prints correctly
<< ss.str().c_str() // Prints correctly
<< cstr2; // Prints correctly (???)
imprime correctement les chaînes?
Je compile dans Visual Studio 2008.
la source
str()
est implémentée de telle sorte que RVO puisse démarrer (ce qui est très probable), le compilateur est autorisé à construire le résultat directement entmp
, élimination du temporaire; et tout compilateur C ++ moderne le fera lorsque les optimisations sont activées. Bien sûr, la solution bind-to-const-reference garantit l'absence de copie, donc peut-être préférable - mais j'ai pensé que cela méritait encore d'être clarifié.Ce que vous faites, c'est créer un fichier temporaire. Ce temporaire existe dans une portée déterminée par le compilateur, de sorte qu'il est suffisamment long pour répondre aux exigences de la destination.
Dès que l'instruction
const char* cstr2 = ss.str().c_str();
est terminée, le compilateur ne voit aucune raison de conserver la chaîne temporaire, et elle est détruite, et ainsi vousconst char *
pointe vers la mémoire libre.Votre déclaration
string str(ss.str());
signifie que le temporaire est utilisé dans le constructeur pour lastring
variablestr
que vous avez placée sur la pile locale, et qu'il reste aussi longtemps que vous le souhaitez: jusqu'à la fin du bloc ou de la fonction que vous avez écrite. Par conséquent, l'const char *
intérieur est toujours une bonne mémoire lorsque vous essayez lecout
.la source
Dans cette ligne:
ss.str()
fera une copie du contenu du stringstream. Lorsque vous appelezc_str()
sur la même ligne, vous référencerez des données légitimes, mais après cette ligne, la chaîne sera détruite, vous laissantchar*
pointer vers une mémoire sans propriétaire.la source
L'objet std :: string retourné par ss.str () est un objet temporaire qui aura une durée de vie limitée à l'expression. Vous ne pouvez donc pas affecter un pointeur à un objet temporaire sans obtenir la corbeille.
Maintenant, il y a une exception: si vous utilisez une référence const pour obtenir l'objet temporaire, il est légal de l'utiliser pour une durée de vie plus longue. Par exemple, vous devriez faire:
De cette façon, vous obtenez la chaîne plus longtemps.
Maintenant, vous devez savoir qu'il existe une sorte d'optimisation appelée RVO qui dit que si le compilateur voit une initialisation via un appel de fonction et que cette fonction retourne un temporaire, il ne fera pas la copie mais rendra simplement la valeur assignée temporaire . De cette façon, vous n'avez pas besoin d'utiliser réellement une référence, c'est seulement si vous voulez être sûr qu'elle ne copiera pas que c'est nécessaire. Alors faire:
serait mieux et plus simple.
la source
Le
ss.str()
temporaire est détruit une fois l'initialisation decstr2
est terminée. Ainsi, lorsque vous l'imprimez aveccout
, la chaîne C qui était associée à cestd::string
temporaire a longtemps été détruite, et vous aurez donc de la chance si elle se bloque et s'affirme, et pas de chance si elle imprime des déchets ou semble fonctionner.La chaîne C vers laquelle
cstr1
pointe, cependant, est associée à une chaîne qui existe toujours au moment où vous effectuez lecout
- donc elle imprime correctement le résultat.Dans le code suivant, le premier
cstr
est correct (je suppose que c'estcstr1
dans le vrai code?). La seconde imprime la chaîne C associée à l'objet chaîne temporairess.str()
. L'objet est détruit à la fin de l'évaluation de l'expression complète dans laquelle il apparaît. L'expression complète est l'cout << ...
expression entière - ainsi, tant que la chaîne C est sortie, l'objet chaîne associé existe toujours. Carcstr2
- c'est de la pure méchanceté qu'elle réussit. Il choisit très probablement en interne le même emplacement de stockage pour le nouveau temporaire qu'il a déjà choisi pour le temporaire utilisé pour l'initialisationcstr2
. Cela pourrait aussi bien s'écraser.Le retour de
c_str()
pointera généralement simplement vers le tampon de chaîne interne - mais ce n'est pas obligatoire. La chaîne pourrait constituer un tampon si son implémentation interne n'est pas contiguë par exemple (c'est bien possible - mais dans le prochain standard C ++, les chaînes doivent être stockées de manière contiguë).Dans GCC, les chaînes utilisent le comptage de références et la copie sur écriture. Ainsi, vous constaterez que ce qui suit est vrai (c'est le cas, du moins sur ma version GCC)
Les deux chaînes partagent le même tampon ici. Au moment où vous changez l'un d'eux, le tampon sera copié et chacun conservera sa copie séparée. Cependant, d'autres implémentations de chaînes font les choses différemment.
la source