73 votes

Destructeur appelé après avoir lancé à partir d'un constructeur

Je pensais qu'en C++, si un constructeur lance une exception, le destructeur de cette "partiellement construit" la classe n'est pas appelé.

Mais il semble qu'il n'est plus vrai en C++11: j'ai compilé le code suivant avec g++ et il imprime "X destructor" de la console. Pourquoi est-ce?

#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;
    }
}

Sortie

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

81voto

Kerrek SB Points 194696

Déléguer constuctors sont en effet une nouvelle fonctionnalité qui introduit une nouvelle destruction de la logique.

Nous revenons de la durée de vie d'un objet: Un objet de la vie commence quand certains constructeur a fini. (Voir 15.2/2. La norme appelle cela le "principal constructeur".) Dans votre cas, c'est le constructeur X(int). La deuxième, la délégation de constructeur X() agit simplement comme une simple fonction de membre de maintenant. Sur la portée de déroulement, les destructeurs de toutes entièrement construit objets sont appelés, et cela inclut x.

Les implications de ce fait, assez profonde: Vous pouvez maintenant mettre "complexe" de la charge de travail dans un constructeur et de profiter pleinement de l'habitude, la propagation d'exception, tant que vous faites votre constructeur de déléguer à un autre constructeur. Une telle conception peut éviter la nécessité pour les différents "init"-les fonctions que l'habitude d'être populaire quand il n'a pas voulu mettre trop de travail régulier, constructeur.

La langue qui définit le comportement que vous voyez n'est:

[C++11: 15.2/2]: [..] De même, si la non-délégation constructeur d'un objet a terminé l'exécution et de la délégation constructeur de cet objet se ferme avec une exception, le destructeur de l'objet sera invoquée. [..]

26voto

Jonathan Wakely Points 45593

Je pensais qu'en C++, si un constructeur lance une exception, le destructeur de cette "partiellement construit" la classe n'est pas appelé.

Mais il semble qu'il n'est plus vrai en C++11

C'est toujours vrai. Rien n'a changé depuis C++03 (pour une certaine valeur de rien ;-) )

Qu'avez-vous pensé est toujours vrai, mais il n'est pas partiellement construit de l'objet lorsque l'exception est levée.

Le C++03 TC1 standard dit (l'emphase est mienne):

Un objet qui est partiellement construit ou partiellement détruites aura destructeurs exécutée pour tous les de son entièrement construite sous-objets, qui est, pour les sous-objets pour lesquels le constructeur a terminé l'exécution, et le destructeur n'a pas encore commencé l'exécution.

c'est à dire un objet qui a terminé son constructeur détruits par l'exécution du destructeur. C'est une bonne règle simple.

Fondamentalement la même règle s'applique en C++11: dès qu' X(int) a retourné, l'objet du "constructeur a terminé l'exécution" de sorte qu'il est entièrement construit, et donc de son destructeur sera exécuté au moment opportun (quand elle est hors de portée ou une exception est levée lors d'un stade ultérieur de ses travaux de construction.) C'est toujours la même règle, dans l'essence.

La délégation corps du constructeur s'exécute après l'autre constructeur et peuvent faire du travail supplémentaire, mais cela ne change pas le fait que l'objet de la construction est terminée, de sorte qu'il est entièrement construit. La délégation constructeur est analogue à une classe dérivée de' constructeur, qui permet d'exécuter du code après une classe de base constructeur de finitions. Dans un certain sens, vous pouvez envisager votre exemple, comme ceci:

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()");    
    }
};

ce n'est pas vraiment comme ça, il n'y a qu'un seul type, mais il est analogue dans la mesure où la X(int) constructeur, le code supplémentaire dans le déléguant constructeur s'exécute, et si qui lève l' X "classe de base" (qui n'est pas vraiment une classe de base) est détruit.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X