Minha pergunta pode ser resumida em: para onde a string retornada fica stringstream.str().c_str()
na memória e por que não pode ser atribuída a const char*
?
Este exemplo de código explicará melhor do que eu posso
#include <string>
#include <sstream>
#include <iostream>
using namespace std;
int main()
{
stringstream ss("this is a string\n");
string str(ss.str());
const char* cstr1 = str.c_str();
const char* cstr2 = ss.str().c_str();
cout << cstr1 // Prints correctly
<< cstr2; // ERROR, prints out garbage
system("PAUSE");
return 0;
}
A suposição que stringstream.str().c_str()
poderia ser atribuída a um const char*
levou a um bug que demorou um pouco para ser rastreado.
Para pontos de bônus, alguém pode explicar por que substituir a cout
declaração por
cout << cstr // Prints correctly
<< ss.str().c_str() // Prints correctly
<< cstr2; // Prints correctly (???)
imprime as cordas corretamente?
Estou compilando no Visual Studio 2008.
fonte
str()
for implementado de forma que o RVO possa entrar em ação (o que é muito provável), o compilador poderá construir o resultado diretamente entrandotmp
, elegendo o temporário; e qualquer compilador C ++ moderno fará isso quando as otimizações estiverem ativadas. Obviamente, a solução de ligação a const-reference garante nenhuma cópia, portanto pode ser preferível - mas achei que ainda vale a pena esclarecer.O que você está fazendo é criar um temporário. Esse temporário existe em um escopo determinado pelo compilador, de modo que seja longo o suficiente para atender aos requisitos de onde está indo.
Assim que a instrução
const char* cstr2 = ss.str().c_str();
é concluída, o compilador não vê razão para manter a cadeia temporária por aí, e ela é destruída, e assim vocêconst char *
está apontando para a memória liberada.Sua declaração
string str(ss.str());
significa que o temporário é usado no construtor para astring
variávelstr
que você colocou na pilha local e que permanece em torno do tempo esperado: até o final do bloco, ou função que você escreveu. Portanto, oconst char *
interior ainda é uma boa memória quando você tenta ocout
.fonte
Nesta linha:
ss.str()
fará uma cópia do conteúdo do stringstream. Ao ligarc_str()
na mesma linha, você fará referência a dados legítimos, mas após essa linha a string será destruída, deixando vocêchar*
apontar para a memória não proprietária.fonte
O objeto std :: string retornado por ss.str () é um objeto temporário que terá um tempo de vida limitado à expressão. Portanto, você não pode atribuir um ponteiro a um objeto temporário sem obter lixo.
Agora, há uma exceção: se você usar uma referência const para obter o objeto temporário, é legal usá-lo por uma vida útil mais longa. Por exemplo, você deve fazer:
Dessa forma, você obtém a corda por mais tempo.
Agora, você precisa saber que existe um tipo de otimização chamado RVO que diz que, se o compilador vir uma inicialização por meio de uma chamada de função e essa função retornar temporariamente, ele não fará a cópia, mas fará com que o valor atribuído seja temporário. . Dessa forma, você não precisa realmente usar uma referência, é apenas se você quiser ter certeza de que não copiará que é necessário. Assim fazendo:
seria melhor e mais simples.
fonte
O
ss.str()
temporário é destruído após a inicialização decstr2
ser concluída. Portanto, quando você o imprimecout
, a string c que foi associada a essestd::string
temporário há muito tempo é destruída e, portanto, você terá sorte se ele travar e afirmar, e não terá sorte se imprimir lixo ou parecer funcionar.A string C para onde
cstr1
aponta, no entanto, está associada a uma string que ainda existe no momento em que você faz o itemcout
- portanto, ele imprime o resultado corretamente.No código a seguir, o primeiro
cstr
está correto (presumo que estejacstr1
no código real?). O segundo imprime a cadeia c associada ao objeto de cadeia temporáriass.str()
. O objeto é destruído ao final da avaliação da expressão completa em que aparece. A expressão completa é acout << ...
expressão inteira - portanto, enquanto a c-string é emitida, o objeto de string associado ainda existe. Poiscstr2
- é pura maldade que ela consegue. Possivelmente, internamente, escolhe o mesmo local de armazenamento para o novo temporário que já escolheu para o temporário usado para inicializarcstr2
. Também poderia falhar.O retorno de
c_str()
geralmente aponta apenas para o buffer interno da string - mas isso não é um requisito. A string pode formar um buffer se sua implementação interna não for contínua, por exemplo (isso é bem possível - mas no próximo padrão C ++, as strings precisam ser armazenadas contiguamente).No GCC, as seqüências usam contagem de referência e cópia na gravação. Assim, você descobrirá que o seguinte é verdadeiro (pelo menos na minha versão do GCC)
As duas seqüências compartilham o mesmo buffer aqui. No momento em que você altera um deles, o buffer é copiado e cada um mantém sua cópia separada. Outras implementações de string fazem coisas diferentes, no entanto.
fonte