Eu tentei chamar ::delete
uma aula operator delete
sobre isso. Mas o destruidor não é chamado.
Eu defini uma classe MyClass
que operator delete
foi sobrecarregada. O global operator delete
também está sobrecarregado. O sobrecarregado operator delete
de MyClass
chamará o global sobrecarregado operator delete
.
class MyClass
{
public:
MyClass() { printf("Constructing MyClass...\n"); }
virtual ~MyClass() { printf("Destroying MyClass...\n"); }
void* operator new(size_t size)
{
printf("Newing MyClass...\n");
void* p = ::new MyClass();
printf("End of newing MyClass...\n");
return p;
}
void operator delete(void* p)
{
printf("Deleting MyClass...\n");
::delete p; // Why is the destructor not called here?
printf("End of deleting MyClass...\n");
}
};
void* operator new(size_t size)
{
printf("Global newing...\n");
return malloc(size);
}
void operator delete(void* p)
{
printf("Global deleting...\n");
free(p);
}
int main(int argc, char** argv)
{
MyClass* myClass = new MyClass();
delete myClass;
return EXIT_SUCCESS;
}
A saída é:
Newing MyClass...
Global newing...
Constructing MyClass...
End of newing MyClass...
Constructing MyClass...
Destroying MyClass...
Deleting MyClass...
Global deleting...
End of deleting MyClass...
Real:
Há apenas uma chamada para o destruidor antes de chamar a sobrecarga operator delete
de MyClass
.
Esperado:
Existem duas chamadas para o destruidor. Um antes de chamar o sobrecarregado operator delete
de MyClass
. Outro antes de chamar o global operator delete
.
c++
delete-operator
expinc
fonte
fonte
MyClass::operator new()
deve alocar memória bruta, de (pelo menos)size
bytes. Não deve tentar construir completamente uma instância deMyClass
. O construtor deMyClass
é executado depoisMyClass::operator new()
. Em seguida, adelete
expressãomain()
chama o destruidor e libera a memória (sem chamar o destruidor novamente). A::delete p
expressão não possui informações sobre o tipo de objetop
apontado para, uma vez quep
é avoid *
, portanto, não pode invocar o destruidor.::delete p;
provoca um comportamento indefinido, pois o tipo de*p
não é o mesmo que o tipo de objeto que está sendo eliminada (nem uma classe base com destrutor virtual)void*
o operando é explicitamente mal formado. [expr.delete] / 1 : " O operando deve ser um ponteiro para o tipo de objeto ou de classe. [...] Isso implica que um objeto não pode ser excluído usando um ponteiro do tipo void porque void não é um tipo de objeto. * "@OP Alterei minha resposta.Respostas:
Você está usando mal
operator new
eoperator delete
. Esses operadores são funções de alocação e desalocação. Eles não são responsáveis por construir ou destruir objetos. Eles são responsáveis apenas por fornecer a memória na qual o objeto será colocado.As versões globais dessas funções são
::operator new
e::operator delete
.::new
e::delete
são novas / delete-expression, assim como sãonew
/delete
, diferentes daquelas::new
e::delete
ignoram sobrecargasoperator new
/ classes específicasoperator delete
.O novo / delete-expression constrói / destrói e aloca / desaloca (chamando o apropriado
operator new
ouoperator delete
antes da construção ou após a destruição).Como sua sobrecarga é responsável apenas pela parte de alocação / desalocação, ela deve chamar
::operator new
e, em::operator delete
vez de::new
e::delete
.O
delete
indelete myClass;
é responsável por chamar o destruidor.::delete p;
não chama o destruidor porquep
tem tipovoid*
e, portanto, a expressão não pode saber qual destruidor chamar. Provavelmente chamará seu substituído::operator delete
para desalocar a memória, embora o uso de umvoid*
operando como para uma expressão de exclusão esteja mal formado (veja a edição abaixo).::new MyClass();
chama seu substituído::operator new
para alocar memória e constrói um objeto nele. O ponteiro para esse objeto é retornado quantovoid*
à nova expressão emMyClass* myClass = new MyClass();
, que então constrói outro objeto nessa memória, encerrando a vida útil do objeto anterior sem chamar seu destruidor.Editar:
Graças ao comentário de @ MM sobre a questão, percebi que um
void*
operando as::delete
é realmente mal formado. ( [expr.delete] / 1 ) No entanto, os principais compiladores parecem ter decidido apenas avisar sobre isso, não erro. Antes de ficar mal formado, usando::delete
umvoid*
comportamento já indefinido, consulte esta pergunta .Portanto, seu programa está mal formado e você não tem garantia de que o código realmente faça o que eu descrevi acima se ele ainda conseguiu compilar.
Conforme apontado por @SanderDeDycker abaixo da resposta, você também tem um comportamento indefinido porque, ao construir outro objeto na memória que já contém um
MyClass
objeto sem chamar primeiro o destruidor desse objeto, você está violando [basic.life] / 5, que o proíbe se o programa depende dos efeitos colaterais do destruidor. Nesse caso, aprintf
declaração no destruidor tem esse efeito colateral.fonte
Suas sobrecargas específicas da classe são feitas incorretamente. Isso pode ser visto em sua saída: o construtor é chamado duas vezes!
Na classe específica
operator new
, chame o operador global diretamente:Da mesma forma, no específico da classe
operator delete
, faça:Consulte a
operator new
página de referência para mais detalhes.fonte
Consulte Referência de CPP :
Excluir (e novo) são responsáveis apenas pela parte 'gerenciamento de memória'.
Portanto, é claro e esperado que o destruidor seja chamado apenas uma vez - para limpar a instância do objeto. Seria chamado duas vezes, todo destruidor teria que verificar se já havia sido chamado.
fonte
operator delete()
função não é a mesma coisa que uma expressão de exclusão. O destruidor é chamado antes daoperator delete()
função ser chamada.