Talvez pareça ser uma pergunta boba, mas eu realmente preciso esclarecer isso:
Isso trará algum perigo para o meu programa?
O const_cast
é necessário?
Se eu alterar os valores dos pointers de input no local, ele funcionará com segurança com std::string
ou criará um comportamento indefinido?
Até agora, a única preocupação é que isso possa afetar a string “some_text” sempre que eu modifico o ponteiro de input e o inutilizo.
std::string some_text = "Text with some input"; char * input = const_cast(some_text.c_str());
Obrigado por me dar algumas dicas, eu gostaria de evitar a filmagem no meu próprio pé
Como um exemplo de mau comportamento: a interação com a implementação Copy On Write do gcc.
#include #include int main() { std::string const original = "Hello, World!"; std::string copy = original; char* c = const_cast(copy.c_str()); c[0] = 'J'; std::cout << original << "\n"; }
Em ação no ideone .
Gelatina, mundo!
O problema ? Como o nome indica, a implementação do std::string
do gcc usa um buffer compartilhado contado na tampa. Quando uma string é modificada, a implementação verificará se o buffer está compartilhado no momento e, se estiver, copie-o antes de modificá-lo, garantindo que outras strings que compartilham esse buffer não sejam afetadas pela nova gravação (daí o nome, cópia na gravação).
Agora, com seu programa maligno, você acessa o buffer compartilhado através de um método const (prometendo não modificar nada), mas você o modifica!
Observe que com a implementação do MSVC, que não usa Copy On Write, o comportamento seria diferente ( "Hello, World!"
Seria corretamente impresso).
Esta é exatamente a essência do comportamento indefinido .
Para modificar um object const
inerentemente ao lançar sua constância usando const_cast
é um comportamento indefinido .
string :: c_str () retorna um const char *
, ie: um ponteiro para uma string constante no estilo c. Tecnicamente, modificar isso resultará em Comportamento Indefinido.
Note que o uso de const_cast
é quando você tem um ponteiro const
para um dado non const e deseja modificar os dados não-constantes.
Simplesmente lançar não trará um comportamento indefinido. Modificando os dados apontados, no entanto, será. ( Veja também ISO 14882: 98 5.2.7-7 ).
Se você quiser um ponteiro para dados modificáveis, você pode ter um
std::vector wtf(str.begin(), str.end()); char* lol= &wtf[0];
O std::string
gerencia sua própria memory internamente, e é por isso que ele retorna um ponteiro para essa memory diretamente, como acontece com a function c_str()
. Isso garante que seja constante para que seu compilador o avise caso tente modificá-lo.
Usar o const_cast dessa forma literalmente elimina essa segurança e é apenas uma prática discutivelmente aceitável se você estiver absolutamente certo de que a memory não será modificada.
Se você não pode garantir isso, então você deve copiar a string e usar a cópia; certamente é muito mais seguro fazer isso em qualquer evento (você pode usar o strcpy
).
Veja o site de referência C ++ :
const char* c_str ( ) const;
“Gera uma seqüência de caracteres terminada com nulo (string C) com o mesmo conteúdo que o object string e a retorna como um ponteiro para uma matriz de caracteres.
Um caractere nulo de terminação é automaticamente anexado.
A matriz retornada aponta para um local interno com o espaço de armazenamento necessário para essa seqüência de caracteres mais seu caractere nulo de terminação, mas os valores nessa matriz não devem ser modificados no programa e só terão a garantia de permanecer inalterados até a próxima chamada a uma function de membro não constante do object string. ”
Sim, isso trará perigo, porque
input
para qualquer c_str
aconteça ser agora, mas se some_text
nunca muda ou desaparece, você vai ficar com um ponteiro que aponta para o lixo. O valor de c_str
é garantido como válido apenas enquanto a string não for alterada. E mesmo, formalmente, somente se você não chamar c_str()
em outras strings também. *input
, está? Isso é um não-não! Isso é uma coisa muito ruim de se fazer. Confira o que std :: string :: c_str () faz e concorda comigo.
Em segundo lugar, considere por que você deseja um access não-constante aos internos da std :: string. Aparentemente, você quer modificar o conteúdo, porque senão você usaria um ponteiro const char. Você também está preocupado que não queira alterar a string original. Por que não escrever
std::string input( some_text );
Então você tem um std :: string que você pode mexer sem afetar o original, e você tem a funcionalidade std :: string em vez de ter que trabalhar com um ponteiro C ++ bruto …
Outro giro sobre isso é que torna o código extremamente difícil de manter. Caso em questão: há alguns anos tive que refatorar algum código contendo funções longas. O autor tinha escrito as assinaturas de function para aceitar parâmetros constantes, mas depois foi const_cast
-los dentro da function para remover a constância. Isso quebrou a garantia implícita dada pela function e tornou muito difícil saber se o parâmetro foi alterado ou não dentro do resto do corpo do código.
Em suma, se você tiver controle sobre a string e achar que precisará alterá-la, torne-a não-const em primeiro lugar. Se você não fizer isso, você terá que pegar uma cópia e trabalhar com isso.
é o UB. Por exemplo, você pode fazer algo assim:
size_t const size = (sizeof(int) == 4 ? 1024 : 2048); int arr[size];
sem qualquer conversão e o comutador não reportará um erro. Mas esse código é ilegal. A moral é que você precisa considerar a ação a cada vez.