Traté de llamar ::delete
para una clase en el operator delete
mismo. Pero el destructor no se llama.
Definí una clase MyClass
que operator delete
ha sido sobrecargada. Lo global operator delete
también está sobrecargado. El sobrecargado operator delete
de MyClass
llamará al global sobrecargado 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;
}
El resultado es:
Newing MyClass...
Global newing...
Constructing MyClass...
End of newing MyClass...
Constructing MyClass...
Destroying MyClass...
Deleting MyClass...
Global deleting...
End of deleting MyClass...
Real:
Solo hay una llamada al destructor antes de llamar al sobrecargado operator delete
de MyClass
.
Esperado:
Hay dos llamadas al destructor. Uno antes de llamar al sobrecargado operator delete
de MyClass
. Otro antes de llamar a lo global operator delete
.
c++
delete-operator
expinc
fuente
fuente
MyClass::operator new()
debe asignar memoria en bruto, de (al menos)size
bytes. No debe intentar construir completamente una instancia deMyClass
. El constructor deMyClass
se ejecuta despuésMyClass::operator new()
. Luego, ladelete
expresión inmain()
llama al destructor y libera la memoria (sin volver a llamar al destructor). La::delete p
expresión no tiene información sobre el tipo dep
puntos de objeto , ya quep
es avoid *
, por lo que no puede invocar al destructor.::delete p;
provoca un comportamiento indefinido ya que el tipo de*p
no es el mismo que el tipo del objeto que se está eliminando (ni una clase base con destructor virtual)void*
operando está explícitamente mal formado. [expr.delete] / 1 : " El operando será de puntero a tipo de objeto o de tipo de clase. [...] Esto implica que un objeto no puede eliminarse usando un puntero de tipo void porque void no es un tipo de objeto. * "@OP He modificado mi respuesta.Respuestas:
Estás haciendo mal uso
operator new
yoperator delete
. Estos operadores son funciones de asignación y desasignación. No son responsables de construir o destruir objetos. Solo son responsables de proporcionar la memoria en la que se colocará el objeto.Las versiones globales de estas funciones son
::operator new
y::operator delete
.::new
y::delete
son nuevas / eliminar-expresiones, como sonnew
/delete
, que difieren de esas, en eso::new
y::delete
omitirán las sobrecargasoperator new
/ específicas de claseoperator delete
.Las nuevas expresiones / delete-build construyen / destruyen y asignan / desasignan (llamando al apropiado
operator new
ooperator delete
antes de la construcción o después de la destrucción).Dado que su sobrecarga solo es responsable de la parte de asignación / desasignación, debe llamar
::operator new
y en::operator delete
lugar de::new
y::delete
.El
delete
indelete myClass;
es responsable de llamar al destructor.::delete p;
no llama al destructor porquep
tiene tipovoid*
y, por lo tanto, la expresión no puede saber a qué destructor llamar. Probablemente llamará a su reemplazo::operator delete
para desasignar la memoria, aunque el uso de unvoid*
operando as para una expresión de eliminación está mal formado (vea la edición a continuación).::new MyClass();
llama a su reemplazado::operator new
para asignar memoria y construye un objeto en él. El puntero a este objeto se devuelve en cuantovoid*
a la nueva expresión enMyClass* myClass = new MyClass();
, que luego construirá otro objeto en esta memoria, terminando la vida útil del objeto anterior sin llamar a su destructor.Editar:
Gracias al comentario de @ MM sobre la pregunta, me di cuenta de que un
void*
como operando::delete
está mal formado. ( [expr.delete] / 1 ) Sin embargo, los principales compiladores parecen haber decidido advertir sobre esto, no por error. Antes de que estuviera mal formado, usando::delete
unvoid*
comportamiento ya indefinido, vea esta pregunta .Por lo tanto, su programa está mal formado y no tiene ninguna garantía de que el código realmente haga lo que describí anteriormente si aún logra compilar.
Como señaló @SanderDeDycker debajo de su respuesta, también tiene un comportamiento indefinido porque al construir otro objeto en la memoria que ya contiene un
MyClass
objeto sin llamar primero al destructor de ese objeto, está violando [basic.life] / 5 que prohíbe hacerlo si el El programa depende de los efectos secundarios del destructor. En este caso, laprintf
declaración en el destructor tiene tal efecto secundario.fuente
Sus sobrecargas específicas de clase se realizan incorrectamente. Esto se puede ver en su salida: ¡se llama al constructor dos veces!
En la clase específica
operator new
, llame al operador global directamente:Del mismo modo, en la clase específica
operator delete
, hacer:Consulte la
operator new
página de referencia para más detalles.fuente
Ver referencia de CPP :
Delete (y new) solo son responsables de la parte de 'gestión de memoria'.
Por lo tanto, está claro y se espera que el destructor solo se llame una vez, para limpiar la instancia del objeto. Se llamaría dos veces, cada destructor tendría que verificar si ya se había llamado.
fuente
operator delete()
función no es lo mismo que una expresión de eliminación. Se llama al destructor antes de llamar a laoperator delete()
función.