Il y a beaucoup d'indicateurs en C ++, mais pour être honnête dans environ 5 ans en programmation C ++ (en particulier avec Qt Framework), je n'utilise que l'ancien pointeur brut:
SomeKindOfObject *someKindOfObject = new SomeKindOfObject();
Je sais qu'il y a beaucoup d'autres indicateurs "intelligents":
// shared pointer:
shared_ptr<SomeKindofObject> Object;
// unique pointer:
unique_ptr<SomeKindofObject> Object;
// weak pointer:
weak_ptr<SomeKindofObject> Object;
Mais je n'ai pas la moindre idée de ce qu'il faut en faire et de ce qu'ils peuvent me proposer en comparaison des indicateurs bruts.
Par exemple, j'ai cet en-tête de classe:
#ifndef LIBRARY
#define LIBRARY
class LIBRARY
{
public:
// Permanent list that will be updated from time to time where
// each items can be modified everywhere in the code:
QList<ItemThatWillBeUsedEveryWhere*> listOfUselessThings;
private:
// Temporary reader that will read something to put in the list
// and be quickly deleted:
QSettings *_reader;
// A dialog that will show something (just for the sake of example):
QDialog *_dialog;
};
#endif
Ceci n’est clairement pas exhaustif, mais pour chacun de ces 3 indicateurs, est-il possible de les laisser "bruts" ou dois-je utiliser quelque chose de plus approprié?
Et dans un deuxième temps, si un employeur lira le code, sera-t-il strict sur le type de pointeurs que j'utilise ou non?
c++
programming-practices
pointers
qt
smart-pointer
CheshireChild
la source
la source
Réponses:
Un pointeur "brut" n'est pas géré. C'est-à-dire la ligne suivante:
... perdra de la mémoire si un accompagnement
delete
n'est pas exécuté au bon moment.auto_ptr
Afin de minimiser ces cas, a
std::auto_ptr<>
été introduit. En raison des limitations de C ++ antérieures à la norme 2011, il est toutefois très facileauto_ptr
de perdre de la mémoire. Cela suffit pour des cas limités, comme celui-ci:L'un de ses cas d'utilisation les plus faibles est celui des conteneurs. En effet, si une copie d'un
auto_ptr<>
est créée et que l'ancienne copie n'est pas soigneusement réinitialisée, le conteneur peut supprimer le pointeur et perdre des données.unique_ptr
En remplacement, C ++ 11 a introduit
std::unique_ptr<>
:Un tel
unique_ptr<>
sera correctement nettoyé, même s'il est passé entre les fonctions. Pour ce faire, il représente sémantiquement la "propriété" du pointeur - le "propriétaire" le nettoie. Cela le rend idéal pour une utilisation dans des conteneurs:Contrairement à
auto_ptr<>
,unique_ptr<>
se comporte bien ici, et lors duvector
redimensionnement, aucun des objets ne sera supprimé accidentellement lors de lavector
copie de son magasin de sauvegarde.shared_ptr
etweak_ptr
unique_ptr<>
est utile, bien sûr, mais il existe des cas où vous souhaitez que deux parties de votre base de code puissent faire référence au même objet et copier le pointeur tout en garantissant un nettoyage correct. Par exemple, une arborescence peut ressembler à ceci lorsque vous utilisezstd::shared_ptr<>
:Dans ce cas, nous pouvons même conserver plusieurs copies d'un nœud racine et l'arborescence sera correctement nettoyée lorsque toutes les copies du nœud racine seront détruites.
Cela fonctionne parce que chacun
shared_ptr<>
conserve non seulement le pointeur sur l'objet, mais également un nombre de références de tous lesshared_ptr<>
objets qui font référence au même pointeur. Lorsqu'un nouveau est créé, le nombre augmente. Quand on est détruit, le compte diminue. Lorsque le compte atteint zéro, le pointeur estdelete
d.Cela pose donc un problème: les structures à double liaison se retrouvent avec des références circulaires. Disons que nous voulons ajouter un
parent
pointeur sur notre arbreNode
:Maintenant, si on enlève un
Node
, il y a une référence cyclique à cela. Il ne sera jamaisdelete
d car son compte de référence ne sera jamais égal à zéro.Pour résoudre ce problème, vous utilisez un
std::weak_ptr<>
:Désormais, les choses fonctionneront correctement et la suppression d'un nœud ne laissera pas de références bloquées au nœud parent. Cependant, il est un peu plus compliqué de marcher dans l’arbre:
De cette façon, vous pouvez verrouiller une référence sur le nœud et vous avez une garantie raisonnable qu'elle ne disparaîtra pas tant que vous y travaillerez, car vous en conservez une
shared_ptr<>
.make_shared
etmake_unique
Maintenant, il y a quelques problèmes mineurs avec
shared_ptr<>
etunique_ptr<>
cela devrait être résolu. Les deux lignes suivantes ont un problème:Si
thrower()
lève une exception, les deux lignes perdront de la mémoire. Et plus que cela,shared_ptr<>
maintient le compte de références loin de l’objet qu’il pointe et cela peut signifier une seconde allocation). Ce n'est généralement pas souhaitable.C ++ 11 fournit
std::make_shared<>()
et C ++ 14 fournitstd::make_unique<>()
pour résoudre ce problème:Maintenant, dans les deux cas, même si
thrower()
une exception est générée, il n'y aura pas de fuite de mémoire. En prime, ilmake_shared<>()
a la possibilité de créer son compte de référence dans le même espace mémoire que son objet géré, ce qui peut être plus rapide et économiser quelques octets de mémoire, tout en vous offrant une garantie de sécurité exceptionnelle!Notes sur Qt
Il convient toutefois de noter que Qt, qui doit prendre en charge les compilateurs antérieurs à C ++ 11, dispose de son propre modèle de récupération de place: de nombreux utilisateurs
QObject
disposent d’un mécanisme leur permettant d’être détruits correctement sans que l’utilisateurdelete
n’en ait besoin .Je ne sais pas comment
QObject
va se comporter quand il sera géré par des pointeurs gérés par C ++ 11, je ne peux donc pas dire que ceshared_ptr<QDialog>
soit une bonne idée. Je n'ai pas suffisamment d'expérience avec Qt, mais je pense que Qt5 a été ajusté pour ce cas d'utilisation.la source
shared_ptr
s'agit d'un objet séparé - une allocation distincte - de l'new
objet ed. Ils existent à différents endroits.make_shared
a la capacité de les regrouper au même endroit, ce qui améliore entre autres la localisation du cache.shared_ptr
est un objet. Et pour gérer un objet, il doit allouer un objet (reference-count (faible + fort) + destructeur).make_shared
permet d'allouer cela et l'objet géré en un seul morceau.unique_ptr
n'utilise pas ceux-ci, il n'y a donc aucun avantage correspondant, mis à part le fait de s'assurer que l'objet est toujours la propriété du pointeur intelligent. En aparté, on peut avoir unshared_ptr
qui possède un objet sous-jacent et représente unnullptr
, ou qui ne possède pas et représente un non-nullpointer.shared_ptr
fait un : 1. Il partage la propriété d'un objet (représenté par un objet interne alloué dynamiquement ayant un compte de référence faible et fort, ainsi qu'un délet). . 2. Il contient un pointeur. Ces deux parties sont indépendantes.make_unique
et lesmake_shared
deux s'assurent que l'objet alloué est placé en toute sécurité dans un pointeur intelligent. De plus,make_shared
permet d'allouer l'objet de propriété et le pointeur géré ensemble.