Estudando a STL me bateu uma dúvida. Qual a duração do valor retornado pelo
método c_str() da std::string? Porque utilizei o C++ Builder por muitos anos
e o método c_str() da AnsiString é problemático no sentido de que eu não
posso fazer isso:
void f( AnsiString &str )
{
const char *tmp;
tmp = str.c_str();
... // Utilizar o tmp pode fazer o programa travar!
}
mas tenho que fazer assim:
void f( AnsiString &str )
{
h( str.c_str() );
}
void h( const char *tmp )
{
...
}
ou assim:
void f( AnsiString &str )
{
char *tmp = new char[ str.Length() + 1 ];
strcpy( tmp, str.c_str() );
...
delete[] tmp;
}
Tenho que ter algum cuidado ao utilizar o método c_str() da std::string? Ou
posso perfeitamente fazer isso:
void f( std::string &str )
{
const char *tmp;
tmp = str.c_str();
... // Posso utilizar o tmp sem medo?
}
Obrigado pela atenção,
Márcio Gil.
Enquanto o objeto string não for destruído E seu conteúdo não for
modificado, você pode usar o buffer sem problemas.
Da mesma forma que o std::vector, o std:string mantém um array alocado
dinamicamente para os caracteres. Quando a string crescer além do
tamanho do array, ele será realocado, portanto o ponteiro antigo vai
estar apontando para o buffer que já foi desalocado.
Utilize o c_str() para passar o buffer apenas para funções de APIs
antigas que não modifiquem a string, /e nunca guarde esse ponteiro/.
Para todos os outros casos passe uma referência ou o próprio objeto
std::string.
[]
Mello
Não exatamente. Veja abaixo.
> Da mesma forma que o std::vector, o std:string mantém um array alocado
> dinamicamente para os caracteres. Quando a string crescer além do
> tamanho do array, ele será realocado, portanto o ponteiro antigo vai
> estar apontando para o buffer que já foi desalocado.
Este conselho não é completamente seguro ou completo.
Em std::vector<T>, você pode fazer operações que modifiquem
std::vector<T> sem problemas enquanto usa um ponteiro
para o primeiro elemento do vetor, desde que nenhuma operação
ultrapasse o capacity do std::vector<T>. Com c_str() do std::string
isto não é garantido, qualquer acesso a funções-membro não-const
podem invalidar o ponteiro retornado por c_str(). O conselho do Pedro
por exemplo é exatamente correto. Mesmo funções não-const como
operator[] podem invalidar c_str() mesmo que não haja modificação
do caractere retornado.
E só para ser um pouco mais pedante, Márcio, tome cuidado
para não usar o ponteiro retornado por c_str() depois que o tempo de
vida do objeto tenha terminado. Isto pode acontecer facilmente
ao se guardar o ponteiro retornado por c_str(). O que nos leva ao
segundo conselho:
> Utilize o c_str() para passar o buffer apenas para funções de APIs
> antigas que não modifiquem a string, /e nunca guarde esse ponteiro/.
> Para todos os outros casos passe uma referência ou o próprio objeto
> std::string.
Este conselho é muito bom. A única necessidade que se pode haver
de usar c_str() ao invés de std::string diretamente é de utilizar essa
string em APIs fixas que já usem char*/const char*. Desde que a
mesma não modifique a c-string retornada por c_str().
> []
> Mello
--
Felipe Magno de Almeida
"The user also cannot rely on its value after a subsequent call on a
non-const function on the string".
Peço perdão pelo meu equívoco.
[]
Mello
Obrigado pelas dicas e pela explicação. Meu objetivo mesmo era só efetuar
leituras no ponteiro retornado pelo método do c_str()... Mas vou explicar
melhor:
A uns quatro anos eu desenvolvi algumas rotinas de manipulação de strings
(SQL) com a AnsiString do C++ Builder e elas eram extremamente lentas. Então
eu resolvi modifica-las para trabalhar com char*, porém precisei manter
compatilibilidade com o código já pronto, de modo que eu simplesmente pegava
o ponteiro com o c_str() e efetuava o processamento. Alguns dias depois eu
tive problemas com travamentos nos programas. Depois de muita aporrinhação
para descobrir a origem do problema, eu encontrei no manual do BCB a
informação que o ponteiro retornado por c_str() era temporário, ou seja,
perdia a validade tão logo o programa pulava para a linha de código
seguinte.
Seria até um teste interessante eu reimplementar estas rotinas com a
std::string e comparar o tempo de processamento com a versão que utiliza
char*. Utilizando o std::string::const_iterator imagino que o desempenho
seja satisfatório.
Márcio Gil.
> -----Mensagem original-----
> De: ccppb...@googlegroups.com Em nome de Pedro Lamarão
faça um teste assim:
int f( AnsiString &str )
{
const char *tmp;
tmp = str.c_str();
... // Utilizar o tmp pode fazer o programa travar!
return str.length(); //<-- não deixe o compilador destruir o teu string
}
(...)
faça um teste assim:
int f( AnsiString &str )
{return str.length(); //<-- não deixe o compilador destruir o teu string
const char *tmp;
tmp = str.c_str();
... // Utilizar o tmp pode fazer o programa travar!
}
Talvez você tenha razão quanto a isto, acho que a função
problemática era declarada assim:
int f( AnsiString str )
{
...
}
Mas a documentação do BCB não fala nada de destrutores:
<----
AnsiString::c_str() returns a non const temporary pointer to the
internal string buffer in the AnsiString object. The pointer is
invalid once the statement in which it is used has finished
executing. That is, don't do something like this:
char* cp = Edit1->Text.c_str();
char* cp2 = strtok( cp, " \t\n" ); // cp may no longer be valid
If you need a persistent pointer, you MUST copy the string into its
own buffer:
char* cp = new char[ Edit1->Text.Length() + 1 ];
strcpy( cp, Edit1->Text.c_str() );
---->
Certamente não tem nada a ver com o método c_str() da std::string.
Na verdade o C++ Builder é o próprio Delphi utilizando sintaxe C++ ao
invés de Pascal :-)
Se não me engano, a maioria das libs do BCB foram escritas em Pascal
e os arquivos header gerados a partir dos arquivos .pas (é essa a
informação presente nos arquivos .hpp da VCL: DO NOT EDIT: machine
generated header)
> pode ser que o que está acontecendo é que ele vê que na função f
> vc só usa o str na 2a linha, e depois disso, nunca mais usa o
> _str_ por isso o compilador acha seguro já destruir o já que
> ninguém mais tem referência à ele. Nunca usei o Builder, então
> não tenho certeza disso...
>
> faça um teste assim:
>
> int f( AnsiString &str )
> {
> const char *tmp;
> tmp = str.c_str();
> ... // Utilizar o tmp pode fazer o programa travar!
>
> return str.length(); //<-- não deixe o compilador destruir o
> // teu string
> }
>
Muito interessante, faz sentido. Porém estou aos poucos abandonando
o BCB para escrever programas o mais fiel possível ao padrão
ISO-C++. Mas obrigado pela dica.
Márcio Gil.