Cosa è std :: string :: c_str () lifetime?

In uno dei miei programmi, devo interfacciarmi con un codice legacy che funziona con const char* .

Diciamo che ho una struttura che assomiglia a:

 struct Foo { const char* server; const char* name; }; 

La mia applicazione di livello superiore si occupa solo di std::string , quindi ho pensato di usare std::string::c_str() per recuperare i puntatori const char* .

Ma qual è la durata di c_str() ?

Posso fare qualcosa di simile senza affrontare un comportamento indefinito?

 { 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 dovrei copiare immediatamente il risultato di c_str() in un altro posto?

Grazie.

Il risultato c_str() diventa non valido se std::string viene distrutto o se viene chiamata una funzione membro non const della stringa. Quindi, di solito vorrai farne una copia se hai bisogno di tenerlo in giro.

Nel caso del tuo esempio, sembra che i risultati di c_str() siano usati in modo sicuro, perché le stringhe non vengono modificate mentre si trovano in c_str() . (Tuttavia, non sappiamo cosa use_foo() fare use_foo() o ~Foo() con quei valori: se copiano le stringhe altrove, dovrebbero fare una copia vera e non solo copiare i puntatori di char .)

Tecnicamente il tuo codice va bene.

MA tu hai scritto in modo tale da rendere facile rompere per qualcuno che non conosce il codice. Per c_str (), l’unico utilizzo sicuro è quando lo si passa come parametro a una funzione. Altrimenti si aprono a problemi di manutenzione.

Esempio 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 } 

Quindi per la manutenzione rendilo ovvio:

Soluzione migliore:

 { // 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); } 

Ma se hai stringhe const non ne hai effettivamente bisogno:

 { char const* server = "my_server"; char const* name = "my_name"; Foo foo; foo.server = server; foo.name = name; use_foo(foo); } 

OK. Per qualche motivo li vuoi come stringhe:
Perché non usarli solo nella chiamata:

 { 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()); } 

È valido finché uno dei seguenti eventi non si verifica nell’object string corrispondente:

  • l’object è distrutto
  • l’object è modificato

Stai bene con il tuo codice a meno che tu non modifichi quegli oggetti string dopo che c_str() sono copiati in foo ma prima che use_foo() chiamato use_foo() .

Il valore di ritorno di c_str () è valido solo fino alla successiva chiamata di una funzione membro non costante per la stessa stringa

Il const char* restituito da c_str() è valido solo fino alla successiva chiamata non const all’object std::string . In questo caso stai bene perché il tuo std::string è ancora in ambito per tutta la vita di Foo e non stai facendo altre operazioni che potrebbero cambiare la stringa mentre usi foo.

Finché la stringa non viene distrutta o modificata, utilizzando c_str () è OK. Se la stringa viene modificata utilizzando un c_str () precedentemente restituito, viene definita l’implementazione.