c++ delete-operator

c++ - ¿Por qué no se llama al destructor en el operador delete?



delete-operator (3)

Intenté llamar a ::delete para una clase en el operator delete of it. Pero el destructor no se llama.

MyClass una clase MyClass cuyo operator delete se ha sobrecargado. La operator delete global del operator delete también está sobrecargada. La operator delete de operator delete sobrecargado de MyClass llamará a la operator delete global sobrecargado.

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 operator delete sobrecargado para operator delete MyClass .

Esperado:

Hay dos llamadas al destructor. Uno antes de llamar al operator delete sobrecargado operator delete MyClass . Otro antes de llamar al operator delete global operator delete .


Está utilizando mal el operator new y el operator 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 expresiones nuevas / delete, al igual que new / delete , que difieren de esas, en que ::new y ::delete omitirán las sobrecargas del operator new específico de la clase operator new / operator delete .

Las nuevas expresiones / delete construyen / destruyen y asignan / desasignan (llamando al operator new apropiado operator new u operator 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 a ::operator new y ::operator delete lugar de ::new y ::delete .

La delete en delete myClass; es responsable de llamar al destructor.

::delete p; no llama al destructor porque p tiene el tipo void* y, por lo tanto, la expresión no puede saber a qué destructor llamar. Probablemente llamará a su ::operator delete reemplazado ::operator delete para desasignar la memoria, aunque el uso de un void* como operando 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 como void* a la nueva expresión en MyClass* myClass = new MyClass(); , que luego construirá otro objeto en esta memoria, finalizando 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 para ::delete realidad está mal formado. ( [expr.delete]/1 ) Sin embargo, los principales compiladores parecen haber decidido solo advertir sobre esto, no por error. Antes de que estuviera mal formado, el uso de ::delete en un void* ya tenía un comportamiento 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 compilarse.

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 objeto MyClass sin llamar primero al destructor de ese objeto, está violando [basic.life]/5 que prohíbe hacerlo si El programa depende de los efectos secundarios del destructor. En este caso, la instrucción printf en el destructor tiene ese efecto secundario.


Sus sobrecargas específicas de clase se realizan incorrectamente. Esto se puede ver en su salida: ¡se llama al constructor dos veces!

En el operator new específico de clase operator new , llame al operador global directamente:

return ::operator new(size);

Del mismo modo, en la operator delete específico de la clase, haga:

::operator delete(p);

Consulte la operator new página de referencia del operator new para obtener más detalles.


Ver referencia de CPP :

operator delete , operator delete[]

Desasigna el almacenamiento previamente asignado por un operator new coincidente operator new . Estas funciones de desasignación se llaman mediante expresiones de eliminación y nuevas expresiones para desasignar la memoria después de destruir (o no construir) objetos con una duración de almacenamiento dinámico. También pueden llamarse utilizando la sintaxis de llamada a función regular.

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.