¿Qué es std :: string :: c_str () de por vida?

100

En uno de mis programas, tengo que interactuar con un código heredado que funciona 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 se ocupa de std::string, así que pensé en usarla std::string::c_str()para recuperar const char*sugerencias.

Pero, ¿de qué dura la vida 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 de c_str()a otro lugar?

Gracias.

ereOn
fuente
Me pasó cuando definí una cadena local en una función y regresé .c_str(). No entendía por qué a veces obtengo solo partes de la cadena, hasta que entendí que const char*no vive para siempre, sino hasta que la cadena se destruye
algo

Respuestas:

85

El c_str()resultado se vuelve inválido si std::stringse destruye o si se llama a una función miembro no constante de la cadena. Por lo tanto, por lo general, querrá hacer una copia si necesita 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ía estar haciendo con esos valores; si copian las cadenas en otro lugar, entonces deberían hacer una copia verdadera , y no solo copiar los charpunteros).

Kristopher Johnson
fuente
El puntero c_str () podría no ser válido si el objeto std :: string es un objeto automático que sale del alcance o en una llamada a una función de creación de subprocesos.
GuruM
¿Puedes explicarme non-const member function of the string is called.?
Mathew Kurian
2
Una "función miembro no constante" es cualquier función miembro que no está marcada con la constpalabra clave. Tal función podría mutar el contenido de la cadena, en cuyo caso la cadena podría necesitar reasignar la memoria para la versión terminada en nulo de la cadena devuelta por c_str(). Por ejemplo, size()y length()son const, para que pueda llamarlos sin preocuparse por el cambio de cadena, pero clear()no es así const.
Kristopher Johnson
23

Técnicamente, su código está bien.

PERO ha escrito de tal manera que es fácil de romper para alguien que no conoce el código. Para c_str (), el único uso seguro es cuando lo pasa 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, hágalo obvio:

Mejor solucion:

{
  // 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 tiene cadenas constantes, en realidad no las necesita:

{
  char const* server = "my_server";
  char const* name   = "my_name";

  Foo foo;
  foo.server = server;
  foo.name   = name;

  use_foo(foo);
}

OKAY. 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());
}
Martin York
fuente
7

Es válido hasta que ocurra una de las siguientes situaciones con el stringobjeto correspondiente :

  • el objeto es destruido
  • el objeto se modifica

Está bien con su código a menos que modifique esos stringobjetos después de que c_str()se copien los correos electrónicos foopero antes de que use_foo()se llame.

diente filoso
fuente
4

El valor de retorno de c_str () es válido solo hasta la próxima llamada de una función miembro no constante para la misma cadena

DumbCoder
fuente
3

La const char*devolución de c_str()solo es válida hasta la próxima llamada no constante al std::stringobjeto. En este caso, está bien porque std::stringtodavía está dentro del alcance de la vida útil de Fooy no está realizando ninguna otra operación que cambie la cadena mientras usa foo.

AJG85
fuente
2

Siempre que la cadena no se destruya o modifique, usar c_str () está bien. Si la cadena se modifica utilizando un c_str () devuelto previamente, se define la implementación.

CharlesB
fuente
2

Para completar, aquí hay una referencia y una cita de cppreference.com :

El puntero obtenido de c_str()puede ser invalidado por:

  • Pasar una referencia no constante a la cadena a cualquier función de biblioteca estándar, o
  • Llamando a las funciones miembro no constante en el string, con exclusión de operator[], at(), front(), back(), begin(), rbegin(), end()y rend().
Victor Sergienko
fuente