79 votes

Destruction d'objet en C ++

Quand est-il exactement des objets détruits en C++, et ça veut dire quoi? Dois-je détruire manuellement, car il n'y a pas de Garbage Collector? Comment faire des exceptions?

(Note: Ceci est destiné à être une entrée à Débordement de Pile du C++ FAQ. Si vous voulez une critique de l'idée de fournir une FAQ dans ce formulaire, puis de la poster sur meta qui a commencé tout cela serait l'endroit pour le faire. Les réponses à cette question sont surveillés en C++ salon, où la FAQ idée a commencé à en premier lieu, de sorte que votre réponse est très probablement le faire lire par ceux qui sont venus avec l'idée.)

98voto

FredOverflow Points 88201

Dans le texte qui suit, je vais distinguer entre les objets dans l'étendue, dont la durée de destruction est statiquement déterminé par leur cadre englobant (fonctions, blocs, des classes, des expressions), et les objets dynamiques, dont l'heure exacte de la destruction n'est généralement pas connue jusqu'à ce que l'exécution.

Tandis que la destruction de la sémantique des objets de classe sont déterminées par les destructeurs, la destruction d'un scalaire objet est toujours un no-op. Plus précisément, la destruction d'un pointeur de variable ne pas détruire la pointee.

Les objets dans l'étendue

automatique des objets

Automatique des objets (communément appelées "variables locales") sont détruits, dans l'ordre inverse de leur définition, lorsque le contrôle de flux laisse la portée de leur définition:

void some_function()
{
    Foo a;
    Foo b;
    if (some_condition)
    {
        Foo y;
        Foo z;
    }  <--- z and y are destructed here
}  <--- b and a are destructed here

Si une exception est levée lors de l'exécution d'une fonction, tous construits précédemment automatique des objets sont détruits avant que l'exception est propagée à l'appelant. Ce processus est appelé le déroulement de pile. Pendant le déroulement de pile, pas d'autres exceptions peuvent laisser les destructeurs de ladite déjà construits automatique des objets. Sinon, la fonction std::terminate est appelé.

Cela mène à l'une des directives les plus importantes en C++:

Les destructeurs ne devrait jamais jeter.

non-local objets statiques

Les objets statiques définies à l'espace de noms de la portée (communément appelées "variables globales") et les données membres statiques sont détruits, dans l'ordre inverse de leur définition, après l'exécution de l' main:

struct X
{
    static Foo x;   // this is only a *declaration*, not a *definition*
};

Foo a;
Foo b;

int main()
{
}  <--- y, x, b and a are destructed here

Foo X::x;           // this is the respective definition
Foo y;

Notez que l'ordre relatif de la construction (et la destruction) de la statique des objets définis dans les différentes unités de traduction n'est pas défini.

Si une exception quitte le destructeur d'un objet statique, la fonction std::terminate est appelé.

local objets statiques

Les objets statiques définies à l'intérieur des fonctions sont construites lorsque (et si) le contrôle de flux qui passe par leur définition pour la première fois.1 Ils sont détruits dans l'ordre inverse, après l'exécution d' main:

Foo& get_some_Foo()
{
    static Foo x;
    return x;
}

Bar& get_some_Bar()
{
    static Bar y;
    return y;
}

int main()
{
    get_some_Bar().do_something();    // note that get_some_Bar is called *first*
    get_some_Foo().do_something();
}  <--- x and y are destructed here   // hence y is destructed *last*

Si une exception quitte le destructeur d'un objet statique, la fonction std::terminate est appelé.

1: il s'agit d'un modèle simplifié. Les détails d'initialisation des objets statiques sont en réalité beaucoup plus compliqué.

classe de base des sous-objets et membre de la sous-objets

Lorsque le contrôle de flux de feuilles le destructeur d'un objet, son membre sous-objets (également connu sous le nom de "membres de données") sont détruits dans l'ordre inverse de leur définition. Après cela, sa classe de base sous-objets sont détruits dans l'ordre inverse de la base spécificateur de liste:

class Foo : Bar, Baz
{
    Quux x;
    Quux y;

public:

    ~Foo()
    {
    }  <--- y and x are destructed here,
};          followed by the Baz and Bar base class subobjects

Si une exception est levée lors de la construction de l'une des Foo's sous-objets, alors toutes ses déjà construit les sous-objets seront détruits avant que l'exception est propagée. L' Foo destructeur, d'autre part, de ne pas être exécuté, depuis l' Foo objet n'a jamais été entièrement construite.

Notez que le destructeur corps n'est pas responsable de la destruction des données membres eux-mêmes. Il vous suffit d'écrire un destructeur si un membre de données est un handle vers une ressource qui doit être libérée lorsque l'objet est détruit (tel qu'un fichier, une douille, une connexion de base de données, un mutex, ou d'un segment de mémoire).

les éléments du tableau

Les éléments du tableau sont détruits dans l'ordre décroissant. Si une exception est levée lors de la construction de la n-ième élément, les éléments de n-1 à 0 sont détruits avant que l'exception est propagée.

les objets temporaires

Un objet temporaire est construit quand un prvalue expression de type classe est évaluée. L'exemple le plus frappant d'un prvalue expression est l'appel d'une fonction qui retourne un objet par valeur, comme T operator+(const T&, const T&). Dans des circonstances normales, l'objet temporaire est détruite lors de la pleine expression que sur le plan lexical contient les prvalue est complètement évaluée:

__________________________ full-expression
              ___________  subexpression
              _______      subexpression
some_function(a + " " + b);
                          ^ both temporary objects are destructed here

La fonction ci-dessus appellent some_function(a + " " + b) est une expression qui ne fait pas partie d'une expression plus grande (au lieu de cela, il est une partie d'une expression-déclaration). Par conséquent, tous les objets temporaires qui sont construits lors de l'évaluation des sous-expressions seront détruits au point-virgule. Il y a deux tels objets temporaires: le premier est construit au cours de la première addition, et le second est construit pendant la seconde plus. Le deuxième objet temporaire sera détruite avant la première.

Si une exception est levée lors de la deuxième plus, le premier objet temporaire seront détruits correctement avant de propagation de l'exception.

Si une référence locale est initialisé avec un prvalue expression, la durée de vie de l'objet temporaire est étendue à la portée de la référence locale, de sorte que vous ne sera pas obtenir un bancales de référence:

{
    const Foo& r = a + " " + b;
                              ^ first temporary (a + " ") is destructed here
    // ...
}  <--- second temporary (a + " " + b) is destructed not until here

Si un prvalue expression de la non-type de classe est évaluée, le résultat est une valeur, pas un objet temporaire. Cependant, un objet temporaire va être construit si la prvalue est utilisé pour initialiser une référence:

const int& r = i + j;

Dynamique des objets et des tableaux

Dans la section suivante, détruisez X signifie "d'abord la destruction des X, puis relâchez-la mémoire sous-jacente". De même, la création de X signifie "premier allouer suffisamment de mémoire et ensuite construire des X-y".

les objets dynamiques

Un objet dynamique créée par p = new Foo est détruit par delete p. Si vous oubliez d' delete p, vous avez une fuite de ressources. Vous ne devriez jamais tenter d'effectuer l'une des opérations suivantes, car elles conduisent toutes à un comportement indéfini:

  • détruire un objet dynamique via delete[] (note entre crochets), free ou tout autre moyen
  • détruire un objet dynamique à plusieurs reprises
  • accéder à un objet dynamique après qu'il a été détruit

Si une exception est levée lors de la construction d'un objet dynamique, la mémoire sous-jacente est libéré avant que l'exception est propagée. (Le destructeur sera pas exécuté avant pour libérer de la mémoire, parce que l'objet n'a jamais été entièrement construite.)

les tableaux dynamiques

Un tableau dynamique créée par p = new Foo[n] est détruit par delete[] p (note entre crochets). Si vous oubliez d' delete[] p, vous avez une fuite de ressources. Vous ne devriez jamais tenter d'effectuer l'une des opérations suivantes, car elles conduisent toutes à un comportement indéfini:

  • détruire un tableau dynamique via delete, free ou tout autre moyen
  • détruire un tableau dynamique à plusieurs reprises
  • accéder à un tableau dynamique après qu'il a été détruit

Si une exception est levée lors de la construction de la n-ième élément, les éléments de n-1 à 0 sont détruits dans l'ordre décroissant, la mémoire sous-jacente est libéré, et l'exception est propagée.

(Vous devriez généralement préfèrent std::vector<Foo> sur Foo* pour les tableaux dynamiques. Il offre une écriture correcte et robuste code beaucoup plus facile.)

comptage de références pointeurs intelligents

Un objet dynamique géré par plusieurs std::shared_ptr<Foo> des objets sont détruits lors de la destruction de la dernière std::shared_ptr<Foo> d'objets impliqués dans le partage qu'objet dynamique.

(Vous devriez généralement préfèrent std::shared_ptr<Foo> sur Foo* pour les objets partagés. Il offre une écriture correcte et robuste code beaucoup plus facile.)

39voto

Loki Astari Points 116129

Le destructeur d'un objet est appelée automatiquement lorsque l'objet de la durée de vie se termine et il est détruit. Vous ne devriez pas l'habitude de les appeler manuellement.

Nous allons utiliser cet objet comme un exemple:

class Test
{
    public:
        Test()                           { std::cout << "Created    " << this << "\n";}
        ~Test()                          { std::cout << "Destroyed  " << this << "\n";}
        Test(Test const& rhs)            { std::cout << "Copied     " << this << "\n";}
        Test& operator=(Test const& rhs) { std::cout << "Assigned   " << this << "\n";}
};

Il y a trois (quatre en C++11) types distincts de l'objet du C++ et le type de l'objet définit les objets de la durée de vie.

  • Statique de la durée de Stockage des objets
  • Automatique de la durée de Stockage des objets
  • Dynamique de la durée de Stockage des objets
  • (En C++11) Fil durée de Stockage des objets

Statique de la durée de Stockage des objets

Ce sont les plus simples et les assimiler à des variables globales. La durée de vie de ces objets est (généralement) la longueur de l'application. Ces sont (généralement) construit avant le principal est saisi et détruit (dans l'ordre inverse de la phase de création) après nous à la sortie principale.

Test  global;
int main()
{
    std::cout << "Main\n";
}

> ./a.out
Created    0x10fbb80b0
Main
Destroyed  0x10fbb80b0

Note 1: Il existe deux autres types de statique de la durée de stockage objet.

statique des variables membres d'une classe.

Ceux-ci sont pour tous les sens et le but de même que des variables globales en termes de durée de vie.

les variables statiques à l'intérieur d'une fonction.

Ce sont paresseusement créé statique de la durée de stockage des objets. Ils sont créés lors de la première utilisation (dans un thread-safe manoir de C++11). Tout comme les autres statique de la durée de stockage des objets s'ils sont détruits lorsque l'application se termine.

L'ordre de construction/destruction

  • L'ordre de construction au sein d'une unité de compilation est bien définie et que le même comme une déclaration.
  • L'ordre de construction entre les unités de compilation n'est pas défini.
  • L'ordre de destruction est l'exact inverse de l'ordre de la construction.

Automatique de la durée de Stockage des objets

Ce sont le type le plus commun d'objets et de ce que vous devriez être en utilisant 99% du temps.

Ce sont les deux principaux types de variables automatiques:

  • les variables locales à l'intérieur d'une fonction/bloc
  • membre des variables à l'intérieur d'une classe ou un tableau.
  • les variables temporaires.

Les Variables Locales

Lorsqu'une fonction/d'un bloc est sorti de toutes les variables déclarées à l'intérieur de cette fonction/bloc sera détruit (dans l'ordre inverse de la création).

int main()
{
     std::cout << "Main() START\n";
     Test   scope1;
     Test   scope2;
     std::cout << "Main Variables Created\n";


     {
           std::cout << "\nblock 1 Entered\n";
           Test blockScope;
           std::cout << "block 1 about to leave\n";
     } // blockScope is destrpyed here

     {
           std::cout << "\nblock 2 Entered\n";
           Test blockScope;
           std::cout << "block 2 about to leave\n";
     } // blockScope is destrpyed here

     std::cout << "\nMain() END\n";
}// All variables from main destroyed here.

> ./a.out
Main() START
Created    0x7fff6488d938
Created    0x7fff6488d930
Main Variables Created

block 1 Entered
Created    0x7fff6488d928
block 1 about to leave
Destroyed  0x7fff6488d928

block 2 Entered
Created    0x7fff6488d918
block 2 about to leave
Destroyed  0x7fff6488d918

Main() END
Destroyed  0x7fff6488d930
Destroyed  0x7fff6488d938

les variables membres

La durée de vie des variables de membre est lié à l'objet qui en est le propriétaire. Lors de l'un des propriétaires de la durée de vie se termine tous ses membres, la durée de vie se termine également. Si vous avez besoin de regarder le lefespan d'un propriétaire qui obéit aux mêmes règles.

Remarque: les Membres sont toujours détruits avant que le propriétaire dans l'ordre inverse de leur création.

  • Ainsi, pour les membres de la classe, ils sont créés dans l'ordre de déclaration
    et détruits dans l'ordre inverse de la déclaration
  • Ainsi, pour les membres du groupe, ils sont créés dans l'ordre 0-->haut de page
    et détruits dans l'ordre inverse en haut-->0

les variables temporaires

Ce sont obejcts qui sont créés comme le résultat d'une expression, mais ne sont pas affectées à une variable. Les variables temporaires sont détruits à l'instar d'autres variables automatiques. C'est juste que à la fin de leur champ d'application est la fin de l' énoncé dans lequel elles sont créées (c'est normalement le ';').

std::string   data("Text.");

std::cout << (data + 1); // Here we create a temporary object.
                         // Which is a std::string with '1' added to "Text."
                         // This object is streamed to the output
                         // Once the statement has finished it is destroyed.
                         // So the temporary no longer exists after the ';'

Remarque: Il existe des situations où la vie de la temporaire peut être prolongé.
Mais ce n'est pas pertinent pour cette simple discussion. Par le temps que vous comprendre que ce document sera une seconde nature pour vous et avant de prolonger la durée de vie d'un temporaire n'est pas quelque chose que vous voulez faire.

Dynamique de la durée de Stockage des objets

Ces objets ont une dynamique de durée de vie et sont créés avec des new et de les détruire avec un appel à l' delete.

int main()
{
    std::cout << "Main()\n";
    Test*  ptr = new Test();
    delete ptr;
    std::cout << "Main Done\n";
}

> ./a.out
Main()
Created    0x1083008e0
Destroyed  0x1083008e0
Main Done

Pour les développeurs qui viennent de déchets ramassés langues cela peut paraître étrange (la gestion de la durée de vie de votre objet). Mais le problème n'est pas aussi mauvais qu'il semble. Il est rare en C++ pour utiliser les objets alloués dynamiquement directement. Nous avons des objets de gestion pour le contrôle de leur durée de vie.

La chose la plus proche à la plupart des autres GC recueilli des langues est l' std::shared_ptr. Cela permet de garder une trace du nombre d'utilisateurs d'un objet créé dynamiquement et quand ils sont tous partis appellera delete automatiquement (je pense à cela comme une meilleure version d'un objet Java).

int main()
{
    std::cout << "Main Start\n";
    std::shared_ptr<Test>  smartPtr(new Test());
    std::cout << "Main End\n";
} // smartPtr goes out of scope here.
  // As there are no other copies it will automatically call delete on the object
  // it is holding.

> ./a.out
Main Start
Created    0x1083008e0
Main Ended
Destroyed  0x1083008e0

Thread durée de Stockage des objets

Ce sont de nouveaux à la langue. Ils sont très bien comme statique de la durée de stockage des objets. Mais plutôt que de vivre la même vie que la demande qu'ils vivent aussi longtemps que le thread d'exécution ils sont associés.

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