C'est exprès que tu définis une conversion implicite?
Version imprimable
Non plus. J'ai tapé le truc à la va-vite car je voulais surtout mettre en évidence l'idiome copy-swap (ce à quoi j'ai magnifiquement échoué parce que j'ai mis un const de trop :oops:)...
J'ai corrigé, et je viens d'en profiter pour rajouter un constructeur par défaut.
re-re-re-corrigé!
Dis, quand on aura fini, on pourra supprimer les postes de corrections ? Ils me dépriment un peu...
(d'un autre côté, je n'ai jamais voulu cacher que c'était codé sur le pouce)
- L'habitude de la différence entre () et (void) en C, je préfère mettre systématiquement (void).
- Et donc, ça, ça marche, ça compilera et ça fera exactement ce qu'on veut que ça fasse ? (hormis le contrôle sur les exceptions)
Code:
1
2
3 explicit CSmartPointer(T *p) : p(p), pr(new int(1)) { }
Je ne sais pas pourquoi mais j'avais en tête que ce n'étais pas possible, j'ai du le vérifier pour le croire :D, mais au final je suppose qu'il est quand même plus clair d'avoir deux noms différents et ça ne coute pas bien cher non... (enfin je suppose que c'est une question de choix personel)...?Citation:
Envoyé par corrector
Je ne suis pas sûr de tout saisir (:oops:), quelle serait le code adapté alors... :Citation:
Envoyé par corrector
Dans ce cas précis, la gestion d'un bloc catch_try ne coûte pas trop cher en terme de performance?Code:
1
2
3
4
5
6
7
8
9
10 CSmartPointer(T *_p) : p(_p) { try{ this->pr = new int(1); } catch(bad_alloc &) { delete p; throw; } }
Juste par curiosité, dans quelle cas ce genre d'exception arrive en général (je n'ai jamais été confronté à ce problème :oops:)?
Et tant qu'à faire, n'est-il pas préférable d'éviter les exceptions justement :Code:
1
2
3 CSmartPointer(T *_p) : p(_p), pr(new(nothrow) int(1)) { }
Dans un constructeur, pas trop.
Je retire ce que j'ai dit sur le nothrow (:oops:) la suite de mon code est basé sur un compteur sencé être alloué correctement donc... voila le code complet de ma classe :Et l'implémentation :Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 template <class T> class CSmartPointer { public: CSmartPointer(T *Pointer = NULL); CSmartPointer(CSmartPointer<T> const &SmartPointer); ~CSmartPointer(); CSmartPointer<T> & operator = (CSmartPointer<T> const &SmartPointer); CSmartPointer<T> & operator = (T *Pointer); bool operator () (); T const * const operator -> () const; T * const operator -> (); T const & operator * () const; T & operator * (); operator T const * const() const; operator T * const(); void Swap(CSmartPointer<T> &SmartPointer); template <class Other> operator CSmartPointer<Other>() const; private: T *m_Pointer; // Pointer on the object int *m_Counter; // Counter on the object pointed };
Je ne suis pas encore bien habitué au mot clé explicit mais dans mon projet j'utilise :Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106 template <class T> inline CSmartPointer<T>::CSmartPointer(T *Pointer) : m_Pointer(Pointer), m_Counter(NULL) { try{ m_Counter = new int(1); } catch(std::bad_alloc &) { SAFE_DELETE(m_Pointer); throw; } } template <class T> inline CSmartPointer<T>::CSmartPointer(CSmartPointer<T> const &SmartPointer) : m_Pointer(SmartPointer.m_Pointer), m_Counter(SmartPointer.m_Counter) { ++(*m_Counter); } template <class T> inline CSmartPointer<T>::~CSmartPointer() { if( --(*m_Counter) == 0 ) { delete m_Pointer; delete m_Counter; } } template <class T> inline CSmartPointer<T> & CSmartPointer<T>::operator = (CSmartPointer<T> const &SmartPointer) { CSmartPointer<T> CopyTmp(SmartPointer); Swap(CopyTmp); return *this; } template <class T> inline CSmartPointer<T> & CSmartPointer<T>::operator = (T *Pointer) { CSmartPointer<T> CopyTmp(Pointer); Swap(CopyTmp); return *this; } template <class T> inline bool CSmartPointer<T>::operator () () { return(m_Pointer!=NULL); } template <class T> inline T const * const CSmartPointer<T>::operator -> () const { Assert( m_Pointer != NULL ); return m_Pointer; } template <class T> inline T * const CSmartPointer<T>::operator -> () { Assert( m_Pointer != NULL ); return m_Pointer; } template <class T> inline T const & CSmartPointer<T>::operator * () const { Assert( m_Pointer != NULL ); return (*m_Pointer); } template <class T> inline T & CSmartPointer<T>::operator * () { Assert( m_Pointer != NULL ); return (*m_Pointer); } template <class T> inline CSmartPointer<T>::operator T const * const() const { return m_Pointer; } template <class T> inline CSmartPointer<T>::operator T * const() { return m_Pointer; } template <class T> inline void CSmartPointer<T>::Swap(CSmartPointer<T> &SmartPointer) { std::swap(m_Pointer, SmartPointer.m_Pointer); std::swap(m_Counter, SmartPointer.m_Counter); } template <class T> template <class Other> inline CSmartPointer<T>::operator CSmartPointer<Other>() const { Other *Pointer = (Other *)m_Pointer; return CSmartPointer<Other>(Pointer); }
Avec AddCamera prenant un smart pointer (de CCamera) en paramètre, dans ce cas il faut bien utiliser le constructeur en tant que conversion implicite non?Code:SceneManager.AddCamera( new CCamera(*this) );
Enfin si vous avez d'autres conseils ou critiques sur le code je suis preneur encore merci à tous :D
NOTE : l'opérateur de conversion n'a pas encore était testé(:oops:), je ne suis pas sur que ce soit très correct (enfin j'y viendrai...)
Je pense que tu peux utiliser le swap aussi sur operator= (T*).
Et aussi, je te pose la question avant corrector: Constructeur implicite, c'est voulu ?
En effet ça semble fonctionner, c'est modifé merci ;)Citation:
Envoyé par Médinoc
En prévision a cette question qui m'a était posé 3fois :mouarf: j'ai laissé ce message :Citation:
Et aussi, je te pose la question avant corrector: Constructeur implicite, c'est voulu ?
Pourquoi cette question revient-elle? Il y a des risques (dans mon cas)?Citation:
Envoyé par babar63
OK. En effet, là, le constructeur implicite est requis, je pense.
Et pour les risques, je ne sais pas trop à part des risques de confusion. J'ai surtout posé la question par mimétisme, parce que corrector la posait...
Dans la classe elle-même, tu peux écrire CSmartPointer à la place de CSmartPointer<T>.
Pourquoi faire un foncteur?
Pourquoi la distinction const/non const?
(Soit rigoureux dans ton explication.)Pourquoi faire une fonction séparée? Tu ne l'appelles qu'une seule fois.
Ces parenthèses sont redondantes.
Ces parenthèses sont redondantes.
Ces parenthèses sont redondantes.
Pourquoi SAFE_DELETE?
En effet, ce n'est pas forcément approprié. Mais ça peut aussi l'être, dépendant des circonstances. Enfin, je crois. Mais en y réfléchissant plus, je ne vois plus trop où, les exemples que j'avaient en tête s'écroulent dès que j'y pense plus d'une seconde...Citation:
Pourquoi la distinction const/non const?
Pour bien faire, il faudrait peut-être deux versions de la classe: Une qui fait des contrôles comme ça, une autre où la constance de l'objet est complètement dissociée de celle du pointeur intelligent...
D'après la FAQ c'est un prédicat si j'ai bien compris, je l'utilise simplement pour tester si mon objet pointé est valide(NULL) ou non.Citation:
Envoyé par corrector
N'est-il pas correct d'utiliser un maximum de const lorsqu'on le peut? Voila un exemple de mon projet qui utilise la fonctionCitation:
Envoyé par corrector
Code:T const * const operator -> () const;
Je suppose que je n'ai pas besoin de montrer d'exemple pour le même opérateur sans tous ces consts :), ce n'est donc pas correct? pourquoi?Code:
1
2
3
4
5 //fonction membre de CSeneManager : std::vector<ILightPtr> const & GetLights() const { return m_Scene->Lights; // Un vecteur de ILightPtr membre de la class }
Je fais de mon mieux mais je suis en train d'apprendre donc je ne peux pas toujours expliquer comme il le faudrait, sans doute je l'ai vu quelques part, ça m'a parut intelligent, j'ai gardé l'idée et je la ressort de temps en temps :oops:.Citation:
Envoyé par corrector
Justement je l'appelais dans mon opérateur :Citation:
Envoyé par corrector
Que j'ai modifié à la suite de la remarque de médinoc, mais j'ai oublié de supprimer la fonction après...(je le fais de suite)Code:CSmartPointer<T> & operator = (T *Pointer);
Je préfère parfois mettre un peu plus de parenthèse, dans la limite du raisonnable je trouve le code plus clair... Est-ce un mauvais réflexe?Citation:
Envoyé par corrector
Euh....:roll:.... Pour le coup j'avoue que je ne vois plus donc sans doute une mauvaise raison :oops:. En revanche le deuxième :Citation:
Envoyé par corrector
est correct non? Je l'utilise pour supprimer m_Pointer si il est NULL et le réinitialiser à NULLCode:
1
2
3
4 catch(std::bad_alloc &) { SAFE_DELETE(m_Pointer); throw; }
Le problème, c'est que ce code suppose que dès que le pointeur est constant, les données pointées le sont aussi.Citation:
N'est-il pas correct d'utiliser un maximum de const lorsqu'on le peut? Voila un exemple de mon projet qui utilise la fonction
<snip>
Je suppose que je n'ai pas besoin de montrer d'exemple pour le même opérateur sans tous ces consts , ce n'est donc pas correct? pourquoi?
Si tu veux un smart pointer vers des données constantes, tu peux en faire un qui retourne toujours un T* et juste faire un CSmartPointer< const int > par exemple...