Propriétés de base des pointeurs intelligents
C'est facile lorsque vous avez des propriétés que vous pouvez attribuer à chaque pointeur intelligent. Il existe trois propriétés importantes.
- pas de propriété du tout
- transfert de propriété
- part de propriété
La première signifie qu'un pointeur intelligent ne peut pas supprimer l'objet, car il ne le possède pas. La seconde signifie qu'un seul pointeur intelligent peut pointer vers le même objet au même moment. Si le pointeur intelligent doit être renvoyé par des fonctions, la propriété est transférée au pointeur intelligent renvoyé, par exemple.
La troisième signifie que plusieurs pointeurs intelligents peuvent pointer vers le même objet en même temps. Cela s'applique à un pointeur brut également, mais les pointeurs bruts manquent d'une caractéristique importante : Ils ne définissent pas s'ils sont posséder ou non. Un pointeur intelligent de partage de propriété supprimera l'objet si chaque propriétaire renonce à l'objet. Ce comportement est souvent nécessaire, c'est pourquoi les pointeurs intelligents à propriété partagée sont largement répandus.
Certains pointeurs intelligents propriétaires ne supportent ni le deuxième ni le troisième. Ils ne peuvent donc pas être retournés par des fonctions ou transmis ailleurs. Ce qui est le plus approprié pour RAII
où le pointeur intelligent est conservé localement et est juste créé pour libérer un objet après qu'il soit sorti de sa portée.
Le partage de la propriété peut être implémenté en ayant un constructeur de copie. Celui-ci copie naturellement un pointeur intelligent et la copie et l'original feront référence au même objet. Le transfert de propriété ne peut pas vraiment être implémenté en C++ actuellement, car il n'y a aucun moyen de transférer quelque chose d'un objet à un autre supporté par le langage : Si vous essayez de retourner un objet depuis une fonction, ce qui se passe est que l'objet est copié. Ainsi, un pointeur intelligent qui implémente le transfert de propriété doit utiliser le constructeur de copie pour implémenter ce transfert de propriété. Cependant, cela empêche son utilisation dans les conteneurs, car les exigences stipulent un certain comportement du constructeur de copie des éléments des conteneurs qui est incompatible avec ce comportement dit de "constructeur mobile" de ces pointeurs intelligents.
C++1x fournit un support natif pour le transfert de propriété en introduisant ce qu'on appelle les "constructeurs de déplacement" et les "opérateurs d'affectation de déplacement". Il est également fourni avec un pointeur intelligent de transfert de propriété appelé unique_ptr
.
Catégorisation des pointeurs intelligents
scoped_ptr
est un pointeur intelligent qui n'est ni transférable ni partageable. Il est juste utilisable si vous avez besoin d'allouer localement de la mémoire, mais assurez-vous qu'il soit libéré à nouveau lorsqu'il sort de sa portée. Mais il peut toujours être échangé avec un autre scoped_ptr, si vous souhaitez le faire.
shared_ptr
est un pointeur intelligent qui partage la propriété (troisième type ci-dessus). Il est compté par référence, ce qui lui permet de savoir quand sa dernière copie sort du champ d'application et de libérer l'objet géré.
weak_ptr
est un pointeur intelligent non propriétaire. Il est utilisé pour référencer un objet géré (géré par un shared_ptr) sans ajouter un compte de référence. Normalement, vous devriez extraire le pointeur brut du shared_ptr et le copier. Mais cela ne serait pas sûr, car vous n'auriez aucun moyen de vérifier quand l'objet a été réellement supprimé. Donc, weak_ptr fournit des moyens en référençant un objet géré par shared_ptr. Si vous avez besoin d'accéder à l'objet, vous pouvez verrouiller la gestion de celui-ci (pour éviter que dans un autre thread un shared_ptr le libère pendant que vous utilisez l'objet) et ensuite l'utiliser. Si le weak_ptr pointe vers un objet déjà supprimé, il vous le fera remarquer en lançant une exception. L'utilisation du weak_ptr est plus avantageuse lorsque vous avez une référence cyclique : Le comptage de références ne peut pas facilement faire face à une telle situation.
intrusive_ptr
est comme un shared_ptr mais il ne garde pas le compte de référence dans un shared_ptr mais laisse l'incrémentation/décrémentation du compte à quelques fonctions d'aide qui doivent être définies par l'objet qui est géré. Ceci a l'avantage qu'un objet déjà référencé (qui a un compte de référence incrémenté par un mécanisme de comptage de référence externe) peut être placé dans un intrusive_ptr - parce que le compte de référence n'est plus interne au pointeur intelligent, mais le pointeur intelligent utilise un mécanisme de comptage de référence existant.
unique_ptr
est un pointeur de transfert de propriété. Vous ne pouvez pas le copier, mais vous pouvez le déplacer en utilisant les constructeurs de déplacement de C++1x :
unique_ptr<type> p(new type);
unique_ptr<type> q(p); // not legal!
unique_ptr<type> r(move(p)); // legal. p is now empty, but r owns the object
unique_ptr<type> s(function_returning_a_unique_ptr()); // legal!
C'est la sémantique à laquelle std::auto_ptr obéit, mais en raison de l'absence de support natif pour le déplacement, elle ne peut les fournir sans pièges. unique_ptr volera automatiquement des ressources à un autre unique_ptr temporaire, ce qui est l'une des caractéristiques clés de la sémantique de déplacement. auto_ptr sera déprécié dans la prochaine version du standard C++ en faveur de unique_ptr. C++1x permettra également de placer des objets qui sont seulement déplaçables mais pas copiables dans des conteneurs. Vous pouvez donc insérer des unique_ptr dans un vecteur par exemple. Je m'arrête ici et vous renvoie à un bel article à ce sujet si vous voulez en savoir plus.