C ++ misturar novo / delete entre libs?
Pergunta
Se eu usar a palavra-chave new
em minha biblioteca (que é construído de forma diferente do meu aplicativo principal), quando eu excluí-lo em meu aplicativo principal com delete
, há uma chance de que eu possa ter uma colisão / erro?
Solução
Sim indeedy. Em particular, você vê problemas com montes debug / release ser diferente, também se colocação seus usos biblioteca nova, ou qualquer personalizado pilha você terá um problema. A questão debug / release é de longe o mais comum embora.
Outras dicas
Depende. Se você está falando de uma biblioteca estática, então você provavelmente vai ser OK - o código será executado no mesmo contexto como o programa principal, usando a mesma biblioteca C ++ runtime. Isto significa que new
e delete
vai usar a mesma pilha.
Se você está falando de uma biblioteca compartilhada (a DLL), então você provavelmente não vai ser OK. O código em execução na DLL pode estar usando uma biblioteca de tempo de execução diferente C ++, o que significa que o layout da pilha será diferente. O DLL pode estar usando uma pilha totalmente diferente.
Chamando delete
(no programa principal) em um ponteiro alocado pelo DLL (ou vice-versa) vai levar a (na melhor das hipóteses) um acidente de imediato ou (na pior das hipóteses) de corrupção de memória que vai demorar um pouco para rastrear.
Você tem um par de opções. A primeira é usar o padrão "método de fábrica" ??para criar e excluir esses objetos:
Foo *CreateFoo();
void DeleteFoo(Foo *p);
Estes devem não ser implementado no cabeçalho do arquivo.
Como alternativa, você pode definir um método Destroy
sobre o objeto:
class Foo
{
~Foo();
public:
virtual void Destroy();
};
... novamente, não implementar isso no arquivo de cabeçalho. Você iria implementá-lo assim:
void Foo::Destroy()
{
delete this;
// don't do anything that accesses this object past this point.
}
Note que o destruidor para Foo é privado, de modo que você tem que Foo::Destroy
chamada.
Microsoft COM faz algo semelhante, onde se define um método Release
que exclui o objeto quando a sua contagem de referência cai para zero.
Sim, você vai. Uma solução simples é fornecer Criar e funções excluir na sua biblioteca que pode ser chamado a partir da aplicação principal. A função Criar irá executar o novo e retornar um ponteiro, que é mais tarde passou para a função Apagar para exclusão.
É um problema que eu só vi no Windows.
Os sistemas Unixish não fazer um hábito de forçar bibliotecas compartilhadas para ligação a diferentes versões da mesma biblioteca dentro do mesmo programa e todos os símbolos carregados são globalmente visível. Isso significa que se um objeto é alocado em uma parte do código e eliminada em outra, ambos estão usando a mesma biblioteca de sistema para fazê-lo.
Eu tenho que dizer, este problema Windows cria com as suas diversas DLLs C tempo de execução é realmente irritante e pouco natural para um programador C. Olhe para a biblioteca C; tem funções como strdup que malloc a corda e esperar que o programador para chamar free () sobre ele. Mas fazer a mesma coisa em sua própria biblioteca no Windows e apenas esperar pela explosão. Você vai ter que esperar, também, porque isso não vai acontecer durante o desenvolvimento, mas só depois que você deu a DLL compilado para algum outro pobre coitado.
Old New Thing cobriu isso antes . Ele também dá uma lista das principais soluções da Microsoft.
Você está certo de que há um problema lá, mas para a maioria dos casos não há uma solução ainda mais simples do que as outras respostas (até agora) têm propostas. Você pode continuar a usar nova e excluir livremente -. Tudo o que você precisa fazer é a sobrecarga de novo e excluir para cada classe em sua biblioteca que pode ser usado através de fronteiras DLL
Pessoalmente, eu só definida uma classe simples para fornecer a funcionalidade necessária:
class NewDelete
{
public:
void *operator new (size_t size);
void operator delete (void *memory);
void *operator new (size_t size, void *ptr);
void operator delete (void *memory, void *ptr);
};
Enquanto essas quatro funções membro são todos definidos na mesma DLL, então qualquer classe que deriva desta classe é automaticamente "DLL-safe" - novo e excluir pode ser usado normalmente sobre eles sem se preocupar com limites de DLL.