Share via


Durée de vie des objets et gestion des ressources (C++ moderne)

Contrairement aux langages managés, C++ n'a pas GC (garbage collection), qui libère automatiquement les ressources de mémoire non-plus-utilisé comme un programme s'exécute.En C++, la gestion des ressources est directement liée à la durée de vie des objets.Ce document décrit les facteurs qui affectent la durée de vie d'objet en C++ et comment le gérer.

C++ n'a pas GC essentiellement parce qu'il ne traite pas les ressources de mémoire non.Uniquement déterministes destructeurs tels que ceux en C++ peuvent gérer des ressources de mémoire et non équitablement.GC dispose également d'autres problèmes, comme le temps système plus élevé dans la mémoire et la consommation du processeur et localité.Mais universalité est un problème fondamental qui ne peut pas être atténué grâce à des optimisations astucieuses.

Concepts

Une chose importante dans la gestion de l'objet du cycle de vie est l'encapsulation, quiconque utilise un objet n'a pas de savoir quelles ressources que cet objet est propriétaire, ou comment s'en débarrasser, ou même si elle possède toutes les ressources du tout.Il doit détruire l'objet.Le langage de base C++ est conçu pour vous assurer que les objets sont détruits, c'est-à-dire aux moments adéquats, comme les blocs sont quittées, dans l'ordre inverse de la construction.Lorsqu'un objet est détruit, ses membres et les bases sont détruites dans un ordre particulier.La langue détruit automatiquement les objets, sauf si vous effectuez des opérations spéciales comme allocation de tas ou nouveau placement.Par exemple, des pointeurs intelligents comme unique_ptr et shared_ptr, et comme conteneurs de la bibliothèque STL (Standard Template Library) vector, encapsuler new/delete et new[]/delete[] dans des objets qui ont des destructeurs.C'est pourquoi il est donc important d'utiliser des pointeurs intelligents et les conteneurs STL.

Un autre concept important dans la gestion de la durée de vie : les destructeurs.Les destructeurs encapsulent la libération de ressources.(Le mnémonique couramment utilisé est RRID, ressource version est Destruction). Une ressource est quelque chose que vous obtenez à partir de « système » et devez à nouveau ultérieurement.La mémoire est la ressource la plus courante, mais il existe également des fichiers, sockets, les textures et autres ressources de mémoire non. "Propriétaire » une ressource signifie que vous pouvez l'utiliser lorsque vous en avez besoin, mais vous avez également pour le libérer lorsque vous avez terminé avec lui.Lorsqu'un objet est détruit, son destructeur libère les ressources auxquelles il appartient.

Le concept final est la Diacylglycérol (graphe acyclique dirigé).La structure de propriété dans un programme constitue un Diacylglycérol.Aucun objet ne peut posséder lui-même — qui est non seulement impossible mais également par nature sans signification.Mais les deux objets peuvent partager la propriété d'un troisième objet.Plusieurs types de liens sont possibles dans un Diacylglycérol comme suit: A est un membre de B (B possède un), magasins c un vector<D> (C est propriétaire de chaque élément D), magasins e un shared_ptr<F> (E partage approprier F, éventuellement avec d'autres objets), et ainsi de suite.Tant qu'il n'y a aucun cycle et tous les liens de la Diacylglycérol sont représenté par un objet qui possède un destructeur (au lieu d'un pointeur brut, poignée ou autre mécanisme), puis les fuites de ressource sont impossibles, car la langue empêche leur.Les ressources sont libérées dès qu'ils sont plus nécessaires, sans un garbage collector exécute.La durée de vie de suivi est exempt de surcharge pour pile étendue, bases, membres et cas apparentés et peu onéreux pour shared_ptr.

Hh438473.collapse_all(fr-fr,VS.110).gifDurée de vie basée sur le tas

Pour la durée de vie d'objet du tas, utilisez des pointeurs intelligents.Utilisation shared_ptr et make_shared que le pointeur par défaut et l'allocateur.Utilisation weak_ptr pour rompre les cycles, effectuez la mise en cache et observer des objets sans affecter ou en supposant que quoi que ce soit sur leur durée de vie.

void func() {

auto p = make_shared<widget>(); // no leak, and exception safe
...
p->draw(); 

} // no delete required, out-of-scope triggers smart pointer destructor

Utilisation unique_ptr les propriétaires uniques, par exemple, dans le pimpl idiome.(Consultez Pimpl pour l'encapsulation de compilation C++ (moderne).) Rendre une unique_ptr la cible principale de tous explicites new expressions.

unique_ptr<widget> p(new widget());

Vous pouvez utiliser les pointeurs bruts pour non propriétaire et d'observation.Un pointeur non propriétaires peut-être intersidéral, mais il ne peut pas de fuite.

class node {
  ...
  vector<unique_ptr<node>> children; // node owns children
  node* parent; // node observes parent, which is not a concern
  ...
};
node::node() : parent(...) { children.emplace_back(new node(...) ); }

Lors de l'optimisation des performances est requise, vous devrez peut-être utiliser bien encapsulées possédant des pointeurs et des appels explicites à supprimer.Un exemple est lorsque vous implémentez votre propre structure de données de bas niveau.

Hh438473.collapse_all(fr-fr,VS.110).gifDurée de vie basée sur la pile

Dans C++ moderne, étendue basée sur pile est un puissant moyen d'écrire du code robuste, car il combine automatique durée de vie de pile et durée de vie des données membres avec haute efficacité — durée de vie de suivi est essentiellement indemne de surcharge.Durée de vie d'objet du tas nécessite une gestion manuelle diligence et peut être la source de fuites de ressource et manque d'efficacité, en particulier lorsque vous travaillez avec des pointeurs bruts.Pensez à ce code, qui montre l'étendue de pile :

class widget {
private:
  gadget g;   // lifetime automatically tied to enclosing object
public:
  void draw();
};

void functionUsingWidget () {
  widget w;   // lifetime automatically tied to enclosing scope
              // constructs w, including the w.g gadget member
  …
  w.draw();
  …
} // automatic destruction and deallocation for w and w.g
  // automatic exception safety, 
  // as if "finally { w.dispose(); w.g.dispose(); }"

Utilisez avec parcimonie de durée de vie statique (statique globale et fonction locales statiques), car des problèmes peuvent survenir.Que se passe-t-il lorsque le constructeur d'un objet global lève une exception ?En général, les pannes d'app d'une manière qui peut être difficile à déboguer.Ordre de construction est problématique pour les objets de la durée de vie statique et n'est pas sécurisé d'accès concurrentiel.Non seulement c'est un problème, construction d'un objet ordre de destruction peut être complexe, particulièrement ceux où le polymorphisme est impliqué.Même si votre objet ou une variable n'est pas polymorphe et n'a pas complexe construction ou destruction de classement, il est toujours le problème de concurrence d'accès thread-safe.Une application multithread ne peut pas modifier en toute sécurité les données dans les objets statiques sans devoir le stockage local des threads, verrous sur les ressources et autres précautions particulières.

Voir aussi

Autres ressources

Accueil vers C++ (C++ moderne)

Guide de référence du langage C++

Référence de la bibliothèque C++ standard