Dans l'un de mes programmes, je dois interfacer avec du code hérité qui fonctionne avec const char*
.
Disons que j'ai une structure qui ressemble à:
struct Foo
{
const char* server;
const char* name;
};
Mon application de niveau supérieur ne traite que de std::string
, J'ai donc pensé à utiliser std::string::c_str()
pour récupérer const char*
Pointeurs.
Mais quelle est la durée de vie de c_str()
?
Puis-je faire quelque chose comme ça sans faire face à un comportement indéfini?
{
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
}
Ou suis-je censé copier immédiatement le résultat de c_str()
vers un autre endroit?
Merci.
Le résultat c_str()
devient invalide si le std::string
Est détruit ou si une fonction membre non const de la chaîne est appelée. Donc, généralement, vous voudrez en faire une copie si vous avez besoin de le garder.
Dans le cas de votre exemple, il semble que les résultats de c_str()
sont utilisés en toute sécurité, car les chaînes ne sont pas modifiées dans cette étendue. (Cependant, nous ne savons pas ce que use_foo()
ou ~Foo()
pourraient faire avec ces valeurs; s'ils copient les chaînes ailleurs, alors ils devraient faire un vrai copier, et pas seulement copier les pointeurs char
.)
Techniquement, votre code est correct.
MAIS vous avez écrit d'une manière qui facilite la rupture pour quelqu'un qui ne connaît pas le code. Pour c_str (), la seule utilisation sûre est lorsque vous la transmettez en tant que paramètre à une fonction. Sinon, vous vous ouvrez à des problèmes de maintenance.
Exemple 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
}
Donc, pour la maintenance, il est évident:
Meilleure solution:
{
// 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);
}
Mais si vous avez des chaînes const, vous n'en avez pas réellement besoin:
{
char const* server = "my_server";
char const* name = "my_name";
Foo foo;
foo.server = server;
foo.name = name;
use_foo(foo);
}
D'ACCORD. Pour une raison quelconque, vous les voulez sous forme de chaînes:
Pourquoi ne pas les utiliser uniquement dans l'appel:
{
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());
}
Il est valide jusqu'à ce que l'une des situations suivantes se produise pour l'objet string
correspondant:
Vous êtes d'accord avec votre code, sauf si vous modifiez ces objets string
après que c_str()
s sont copiés dans foo
mais avant que use_foo()
soit appelé.
La valeur de retour de c_str () n'est valide que jusqu'au prochain appel d'une fonction membre non constante pour la même chaîne
Le const char*
Renvoyé par c_str()
n'est valide que jusqu'au prochain appel non const à l'objet std::string
. Dans ce cas, tout va bien parce que votre std::string
Est toujours à la portée de la vie de Foo
et vous ne faites aucune autre opération qui changerait la chaîne lors de l'utilisation de foo.
Pour être complet, voici un référence et citation de cppreference.com :
Le pointeur obtenu à partir de
c_str()
peut être invalidé par:
- Passer une référence non const à la chaîne à n'importe quelle fonction de bibliothèque standard, ou
- Appel de fonctions membres non const sur la fonction
string
, à l'exclusion deoperator[]
,at()
,front()
,back()
,begin()
,rbegin()
,end()
etrend()
.
Tant que la chaîne n'est pas détruite ou modifiée, l'utilisation de c_str () est OK. Si la chaîne est modifiée à l'aide d'un c_str () précédemment renvoyé, l'implémentation est définie.