ejemplo destructores delete constructores c++ exception c++11

destructores - delete c++



Destructor llamado después de tirar desde un constructor (2)

Solía ​​pensar que en C ++, si un constructor lanza una excepción, el destructor de esta clase "parcialmente construida" no se llama.

Pero parece que ya no es verdad en C ++ 11

Sigue siendo cierto. Nada ha cambiado desde C ++ 03 (por algún valor de nada ;-))

Lo que pensaste sigue siendo cierto, pero no existe un objeto parcialmente construido cuando se lanza la excepción.

El estándar C ++ 03 TC1 dice (énfasis mío):

Un objeto que está parcialmente construido o parcialmente destruido tendrá destructores ejecutados para todos sus subobjetos totalmente construidos, es decir, para subobjetos para los cuales el constructor ha completado la ejecución y el destructor aún no ha comenzado la ejecución.

es decir, cualquier objeto que haya completado su constructor será destruido al ejecutar el destructor. Esa es una buena regla simple.

Fundamentalmente, la misma regla se aplica en C ++ 11: tan pronto como X(int) ha regresado, el "constructor del objeto ha completado la ejecución" por lo que está completamente construido, por lo que su destructor se ejecutará en el momento apropiado (cuando se apaga) de alcance o una excepción se lanza durante una etapa posterior de su construcción.) Sigue siendo la misma regla, en esencia.

El cuerpo del constructor delegante se ejecuta después del otro constructor y puede hacer un trabajo extra, pero eso no cambia el hecho de que la construcción del objeto haya finalizado, por lo que está completamente construido. El constructor delegante es análogo al constructor de una clase derivada, que ejecuta más código después de que termina el constructor de una clase base. En cierto sentido, puedes considerar que tu ejemplo sea así:

class X { public: X(int a) { cout << "X::X(" << a << ")" << endl; } ~X() { cout << "X destructor" << endl; } }; class X_delegating : X { public: X_delegating() : X(10) { throw runtime_error("Exception thrown in X::X()"); } };

no es así, solo hay un tipo, pero es análogo en la medida en que se ejecuta el constructor X(int) , luego se ejecuta código adicional en el constructor delegante, y si eso arroja la X "clase base" (que no es realmente una clase base) se destruye.

Solía ​​pensar que en C ++, si un constructor lanza una excepción, el destructor de esta clase "parcialmente construida" no se llama.

Pero parece que ya no es verdad en C ++ 11: compilé el siguiente código con g ++ e imprime " X destructor " en la consola. ¿Por qué es esto?

#include <exception> #include <iostream> #include <stdexcept> using namespace std; class X { public: X() : X(10) { throw runtime_error("Exception thrown in X::X()"); } X(int a) { cout << "X::X(" << a << ")" << endl; } ~X() { cout << "X destructor" << endl; } }; int main() { try { X x; } catch(const exception& e) { cerr << "*** ERROR: " << e.what() << endl; } }

Salida

Standard out: X::X(10) X destructor Standard error: *** ERROR: Exception thrown in X::X()


Delegar constuctors es de hecho una nueva característica que introduce una nueva lógica de destrucción.

Permítanos revisar la vida útil de un objeto: la duración de un objeto comienza cuando algún constructor ha terminado. (Véase 15.2 / 2. El estándar lo llama el "constructor principal"). En su caso, este es el constructor X(int) . El segundo, delegar el constructor X() actúa como una simple función de miembro ahora. Al desenrollar el alcance, se invocan los destructores de todos los objetos totalmente construidos, y esto incluye x .

Las implicaciones de esto son en realidad bastante profundas: ahora puede colocar cargas de trabajo "complejas" en un constructor y aprovechar al máximo la propagación de excepciones habitual, siempre que haga que su constructor delegue en otro constructor. Tal diseño puede obviar la necesidad de varias funciones "init" que solían ser populares cuando no se deseaba poner demasiado trabajo en un constructor regular.

El lenguaje específico que define el comportamiento que está viendo es:

[C++11: 15.2/2]: [..] Del mismo modo, si el constructor no delegante para un objeto ha completado la ejecución y un constructor delegante para ese objeto sale con una excepción, se invocará el destructor del objeto. [..]