Mi pregunta se puede resumir en: ¿dónde se devuelve la cadena de la stringstream.str().c_str()
memoria en vivo y por qué no se puede asignar a una const char*
?
Este ejemplo de código lo explicará mejor de lo que puedo
#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;
}
La suposición que stringstream.str().c_str()
podría asignarse a un const char*
condujo a un error que me llevó un tiempo rastrear.
Para obtener puntos de bonificación, ¿alguien puede explicar por qué reemplazar la cout
declaración con
cout << cstr // Prints correctly
<< ss.str().c_str() // Prints correctly
<< cstr2; // Prints correctly (???)
imprime las cadenas correctamente?
Estoy compilando en Visual Studio 2008.
fuente
str()
se implementa de tal manera que RVO pueda entrar en acción (lo cual es muy probable), el compilador puede construir el resultado directamente dentrotmp
, eludiendo lo temporal; y cualquier compilador de C ++ moderno lo hará cuando las optimizaciones estén habilitadas. Por supuesto, la solución bind-to-const-reference garantiza la no copia, por lo que puede ser preferible, pero pensé que aún vale la pena aclararlo.Lo que estás haciendo es crear un temporal. Ese temporal existe en un ámbito determinado por el compilador, de modo que es lo suficientemente largo para satisfacer los requisitos de hacia dónde se dirige.
Tan pronto como
const char* cstr2 = ss.str().c_str();
se completa la declaración , el compilador no ve ninguna razón para mantener la cadena temporal, y se destruye, y por lo tanto,const char *
está apuntando a la memoria liberada.Su declaración
string str(ss.str());
significa que el temporal se usa en el constructor para lastring
variablestr
que ha puesto en la pila local, y que permanece durante todo el tiempo que esperaría: hasta el final del bloque o la función que ha escrito. Por lo tanto, elconst char *
interior sigue siendo buena memoria cuando prueba elcout
.fuente
En esta linea:
ss.str()
hará una copia del contenido de la secuencia de cadenas. Cuando llamec_str()
a la misma línea, hará referencia a datos legítimos, pero después de esa línea, la cadena se destruirá, dejandochar*
que apunte a la memoria no deseada.fuente
El objeto std :: string devuelto por ss.str () es un objeto temporal que tendrá un tiempo de vida limitado a la expresión. Por lo tanto, no puede asignar un puntero a un objeto temporal sin obtener basura.
Ahora, hay una excepción: si usa una referencia constante para obtener el objeto temporal, es legal usarlo durante un tiempo de vida más amplio. Por ejemplo, debes hacer:
De esa manera obtienes la cuerda por más tiempo.
Ahora, debe saber que hay un tipo de optimización llamada RVO que dice que si el compilador ve una inicialización a través de una llamada de función y esa función devuelve un temporal, no hará la copia sino que hará que el valor asignado sea temporal . De esa manera, no necesita utilizar una referencia, solo si desea asegurarse de que no copiará que es necesario. Al hacer eso:
Sería mejor y más simple.
fuente
El
ss.str()
temporal se destruye después decstr2
completar la inicialización . Entonces, cuando lo imprimecout
, la cadena en C que estaba asociada con esestd::string
temporal ha sido destruida por mucho tiempo, y por lo tanto tendrá suerte si se bloquea y afirma, y no tendrá suerte si imprime basura o parece funcionar.cstr1
Sin embargo, la cadena C a la que apunta está asociada con una cadena que todavía existe en el momento en que lo hacecout
, por lo que imprime correctamente el resultado.En el siguiente código, el primero
cstr
es correcto (¿supongo que estácstr1
en el código real?). El segundo imprime la cadena c asociada con el objeto de cadena temporalss.str()
. El objeto se destruye al final de evaluar la expresión completa en la que aparece. La expresión completa es lacout << ...
expresión completa , por lo tanto, mientras se emite la cadena c, el objeto de cadena asociado todavía existe. Porquecstr2
es pura maldad lo que tiene éxito. Posiblemente, internamente elige la misma ubicación de almacenamiento para el nuevo temporal que ya eligió para el temporal utilizado para inicializarcstr2
. También podría estrellarse.El retorno de
c_str()
generalmente solo apuntará al búfer de cadena interno, pero eso no es un requisito. La cadena podría formar un búfer si su implementación interna no es contigua, por ejemplo (eso es muy posible, pero en el próximo estándar de C ++, las cadenas deben almacenarse contiguamente).En GCC, las cadenas usan recuento de referencias y copia en escritura. Por lo tanto, encontrará que lo siguiente es cierto (lo hace, al menos en mi versión de GCC)
Las dos cadenas comparten el mismo búfer aquí. En el momento en que cambie uno de ellos, el búfer se copiará y cada uno tendrá su copia separada. Sin embargo, otras implementaciones de cadenas hacen cosas diferentes.
fuente