¿Cuál es la vida útil del resultado de std::string::c_str()?
En uno de mis programas, tengo que interactuar con algún código heredado que funcione con const char*
.
Digamos que tengo una estructura que se parece a:
struct Foo
{
const char* server;
const char* name;
};
Mi aplicación de nivel superior solo trata con std::string
, así que pensé en usarla std::string::c_str()
para recuperar const char*
punteros.
¿ Pero cuál es la vida útil de c_str()
?
¿Puedo hacer algo como esto sin enfrentar un comportamiento indefinido?
{
std::string server = "my_server";
std::string name = "my_name";
Foo foo;
foo.server = server.c_str();
foo.name = name.c_str();
// We use foo
use_foo(foo);
// Foo is about to be destroyed, before name and server
}
¿O se supone que debo copiar inmediatamente el resultado c_str()
a otro lugar?
El c_str()
resultado deja de ser válido si se std::string
destruye o si se llama a una función miembro no constante de la cadena. Por lo tanto, normalmente querrás hacer una copia si necesitas conservarla.
En el caso de su ejemplo, parece que los resultados de c_str()
se usan de manera segura, porque las cadenas no se modifican mientras están en ese alcance. (Sin embargo, no sabemos qué use_foo()
o ~Foo()
podríamos estar haciendo con esos valores; si copian las cadenas en otro lugar, entonces deberían hacer una copia verdadera , y no solo copiar los char
punteros).
Técnicamente tu código está bien.
PERO ha escrito de tal manera que sea fácil de descifrar para alguien que no conozca el código. Para c_str() el único uso seguro es cuando lo pasas como parámetro a una función. De lo contrario, se expone a problemas de mantenimiento.
Ejemplo 1:
{
std::string server = "my_server";
std::string name = "my_name";
Foo foo;
foo.server = server.c_str();
foo.name = name.c_str();
//
// Imagine this is a long function
// Now a maintainer can easily come along and see name and server
// and would never expect that these values need to be maintained as
// const values so why not re-use them
name += "Martin";
// Oops now its broken.
// We use foo
use_foo(foo);
// Foo is about to be destroyed, before name and server
}
Entonces, para el mantenimiento, hazlo obvio:
Mejor solución:
{
// Now they can't be changed.
std::string const server = "my_server";
std::string const name = "my_name";
Foo foo;
foo.server = server.c_str();
foo.name = name.c_str();
use_foo(foo);
}
Pero si tienes cadenas constantes, en realidad no las necesitas:
{
char const* server = "my_server";
char const* name = "my_name";
Foo foo;
foo.server = server;
foo.name = name;
use_foo(foo);
}
DE ACUERDO. Por alguna razón, los quiere como cadenas: ¿
por qué no usarlos solo en la llamada?
{
std::string server = "my_server";
std::string name = "my_name";
// guaranteed not to be modified now!!!
use_foo(Foo(server.c_str(), name.c_str());
}
Es válido hasta que ocurra una de las siguientes situaciones con el string
objeto correspondiente:
- el objeto es destruido
- el objeto se modifica
Está bien con su código a menos que modifique esos string
objetos después c_str()
de copiarlos foo
pero antes de use_foo()
llamarlos.
El valor de retorno de c_str() es válido solo hasta la siguiente llamada de una función miembro no constante para la misma cadena