Você pode explicar esse problema de exclusão C ++?
-
23-09-2019 - |
Pergunta
Eu tenho o seguinte código:
std::string F()
{
WideString ws = GetMyWideString();
std::string ret;
StringUtils::ConvertWideStringToUTF8(ws, ret);
return ret;
}
O WideString é uma classe de terceiros, assim como Stringutils. Eles são uma caixa preta para mim. O segundo parâmetro é passado por referência.
Quando eu passo pelo depurador a linha return ret
joga um pop -up desagradável (Visual C ++) dizendo que a pilha pode ser corrompida. Após uma cópia de exame mais detalhada da string que é devolvida é bom, mas a exclusão de ret
falha. ret
contém valor correto antes do retorno.
O que a função de conversão poderia fazer para causar isso? Alguma ideia para consertar?
Atualizar:
- O projeto em si é uma DLL
- Stringutils é um lib
- O projeto é compilado contra CRT multithread (não depuração, não DLL)
- O programa parece correr bem quando executado fora do Visual Studio
Solução
- Se Stringutils foi compilado separadamente (por exemplo, com uma versão diferente do compilador), você pode ter um conflito no layout do objeto.
- Se o Stringutils estiver em uma DLL, você deve garantir que ele e o programa principal sejam compilados para usar a biblioteca padrão em uma DLL. Caso contrário, cada módulo (executável e DLL) terá seu próprio heap. Quando Stringutils tenta brincar com dados na string que foi alocada a partir de uma pilha diferente, coisas ruins acontecem.
Outras dicas
O designer de Stringutils projetou uma API muito ruim. Nenhum dos tipos de biblioteca padrão modelo deve ser usado na interface pública da API. std::string
está explodido em linha. Portanto, se o compilador e as bibliotecas que você está usando não for exatamente o mesmo compilador e bibliotecas usadas pelo implementador de Stringutils, os tipos podem e provavelmente serão diferentes. Fundamentalmente, o implementador de Stringutils falhou em separar a interface da implementação.
Uma ilustração do problema. Suponha que você esteja usando o MSVC 9.0 SP1 e estou usando o MSVC 8.0. No meu compilador, a implementação do std :: string pode ser assim:
class string
{
// : : stuff
private:
int someInt_;
char* someBuf_;
};
... mas no seu compilador pode parecer diferente:
class string
{
// : : stuff
private:
void* impl_;
};
Se eu escrever uma função da biblioteca:
void DoSomethingWithAString(std::string& str);
... e você chama isso, o sizeof(string)
no seu código será diferente do sizeof(string)
no meu código. Os tipos não são os mesmos.
Você realmente só tem 2 soluções para o seu problema:
1) [Preferido] Obtenha o implementador de Stringutils para corrigir seu código quebrado.
2) Substitua a biblioteca usada pelo seu compilador para corresponder à biblioteca usada pelo implementador da Stringutil. Você pode conseguir isso usando o mesmo compilador no mesmo nível de patch que o implementador usado, assumindo que ele não substituiu a implementação da biblioteca padrão.
Editar: 3) Uma terceira opção seria parar de usar StringUtils. Honestamente, é provavelmente isso que eu faria.
Do pequeno código que você mostra, suponho StringUtils::ConvertWideStringToUTF8()
Leva um std::string&
como um segundo parâmetro. Dado isso, não vejo como seu código pode causar uma corrupção de heap.
Observe, no entanto, que a ligação das bibliotecas C ++ em geral só funciona quando o código foi compilado usando o mesmo compilador e as mesmas configurações do compilador.
Seu uso de StringUtils
e WideString
Faz parecer que você está usando o C ++ Builder. Você está tentando misturar um módulo C ++ Builder e um módulo Visual C ++? Nesse caso, você definitivamente veria os problemas que descreveu.
Você não pode passar um C ++ visual std::string
Para uma função do construtor C ++ porque o código do construtor C ++ assumirá que o parâmetro usa o C ++ Builder's std::string
definição. As classes podem ter campos diferentes, e os campos que eles têm em comum podem estar em uma ordem diferente.
Mesmo que as classes tenham as mesmas definições, os módulos ainda usarão diferentes gerentes de memória. A função chamada alocará memória para o novo conteúdo da string usando seu gerenciador de memória e o chamador usará seu próprio gerenciador de memória para tentar liberar o conteúdo da string posteriormente.