Bonjour à tous,
Je développe depuis un petit bout de temps un moteur de jeu, et j'ai décidé récemment de me mettre aux smart pointers.
En effet, mon code regorge de pointeurs nus, et pour le moment, il faut jeter un coup d'œil à la doc pour savoir quoi faire de ces pointeurs (sont-ils gérés par les classes qui me les ont donnés ? est-ce que la responsabilité m'a été transférée ? si oui, qu'est ce que je dois faire pour les détruires correctement ?).
Dans l'idéal, j'aimerai que le pointeur soit "autodocumenté", c'est à dire qu'on peut répondre aux questions ci-dessus rien qu'avec le type du pointeur.
Logiquement donc, je me suis intéressé aux smart pointers, et comme j'aime comprendre ce que je fais, j'ai écrit mes propres classes (en m'inspirant de celles de boost). Pour l'instant, je ne dispose que d'un pointeur à comptage de référence (qu'on va appeler ref_ptr) et du weak pointer associé (weak_ptr), et un problème ce pose déjà :
Admettons que je dispose d'une fonction membre de ce style :
L'Object retourné ne devra pas être détruit par l'utilisateur (ou, si nécessaire, par le biais d'une fonction de Mere). Je pense donc que retourner un weak_ptr serait une bonne solution : on signifie clairement à l'utilisateur que la responsabilité ne lui a pas été transférée.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 Object* Mere::AddObject( Object* pBase ) { if (pBase) { Object* pObj = new Object(*pBase); // Constructeur de copie lObjectList_.PushBack(pObj); // On stocke l'objet nouvellement créé return pObj; // ... et on le retourne, ça ne mange pas de pain } else return NULL; }
En revanche, la fonction AddObject a besoin d'un pointeur vers un Object, mais elle ne le garde pas en mémoire : elle en a besoin juste pour faire son traitement. En théorie, on prendrait là aussi un weak_ptr, ce qui donnerai :
Mais finalement, la fonction AddObject se fiche qu'on lui donne un weak_ptr, un ref_ptr ou un pointeur nu : tout ce dont elle a besoin, c'est d'un pointeur (valide de préférence).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 weak_ptr<Object> Mere::AddObject( weak_ptr<Object> pBase ) { if (ref_ptr<Object> pLocked = pBase.Lock()) { ref_ptr<Object> pObj = ref_ptr<Object>(new Object(*pLocked)); lObjectList_.PushBack(pObj); return pObj.CreateWeak(); } else return weak_ptr<Object>(); }
Alors question : est-ce qu'il faut que j'écrive autant de versions de cette fonction que j'ai de type de pointeurs ?
Parce que ce code là fonctionnerait tout aussi bien :
... avec une garantie en moins sur la validité de pBase (il peut pointer vers une zone mémoire déjà libérée).
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11 weak_ptr<Object> Mere::AddObject( Object* pBase ) { if (pBase) { ref_ptr<Object> pObj = ref_ptr<Object>(new Object(*pBase)); lObjectList_.PushBack(pObj); return pObj.CreateWeak(); } else return weak_ptr<Object>(); }
Autre situation :
Quel type de pointeur utiliser ici ? Un ref_ptr n'aurait pas de sens (un objet fils n'est pas responsable de la durée de vie de son parent), et un weak_ptr (qui serait déjà plus logique) nécessite que toutes les Frames soient stockées sous forme de ref_ptr.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15 class Frame { public : //... constructeur, destructeur, blablabla void SetParent(Frame* pParent) { pParent_ = pParent; } private : Frame* pParent_; };
De plus, on peut se retrouver dans le cas gênant :
... qui nécessite l'horrible astuce de "enable_shared_from_this".
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 void Frame::AddChild(weak_ptr<Frame> pFrame) { if (ref_ptr<Frame> pLocked = pFrame.Lock()) { pLocked->SetParent(this); // Oups, "this" est un pointeur nu } }
Donc pour résumer, j'ai l'impression que les smart pointers ne sont pas une solution magique qui fonctionne dans tous les cas (cf l'exemple juste au dessus : on a moins de problèmes avec les pointeurs nus...).
Je suppose donc qu'il faut utiliser smart pointers et pointeurs nus en conjonction.
Mais comme on l'a vu dans le premier exemple, est-ce que les deux sont vraiment compatibles ?
Extraire le pointeur nu d'un ref_ptr est toujours un peu dangereux.
Créer un ref_ptr à partir d'un pointeur nu déjà existant l'est encore plus.
Comme vous le voyez, je suis un peu perdu, et j'aimerai bien savoir comment vous vous en sortez
Merci d'avance (et désolé pour le pavé).
Partager