Bonjour à tous,
Je viens à vous car je me heurte à un problème que je n'arrive pas à comprendre.
Je tente d'implémenter une classe singleton Template réutilisable ; en m'inspirant de ce que je trouve sur le net, j'arrive à une première implémentation (non encore thread-safe, mais c'est pas la question), avec des pointeurs classiques, qui marche :
et le main pour tester :
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
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 #ifndef DEFINE_SINGLETON #define DEFINE_SINGLETON #include <iostream> // Classe de design pattern Singleton template <typename T> class Singleton { protected : // Pointeur vers l'instance unique static T* singletonPtr; // Constructeur et destructeur privés pour éviter l'appel depuis l'extérieur. Singleton () {} ~Singleton() {} public : // Méthode statique pour accéder à l'instance unique du Singleton ; celle-ci est // construite si elle n'était pas encore allouée. static T* getInstance() { if ( singletonPtr == nullptr ) singletonPtr = new T; return singletonPtr; } // Méthode statique pour détruire l'instance unique de T static void kill() { if ( singletonPtr != nullptr) delete singletonPtr; singletonPtr = nullptr; } }; // définition du singleton statique, intialisé sur nullptr template <typename T> T* Singleton<T>::singletonPtr = nullptr; // Classe test utilisant la classe ci-dessus class Test : public Singleton<Test> { friend class Singleton<Test>; private : // Constructeur et destructeur private pour éviter l'instanciation/destruction Test() {} ~Test(){} // Constructeur de copie et opérateur d'affectation interdits Test(const Test& other) = delete; Test operator=(const Test& other) = delete; public : // une méthode quelconque void doSomething() { std::cout << "doSomething" << std::endl; } }; #endif // ndef DEFINE_SINGLETON
Ceci compile et marche parfaitement.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 #include <iostream> #include "SingletonSP.hpp" int main() { Test::getInstance()->doSomething(); Test::kill(); return 0; };
Ensuite, j'essaie une version avec des shared_ptr :
et le main à peine modifié :
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
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 #ifndef DEFINE_SINGLETON #define DEFINE_SINGLETON #include <iostream> #include <memory> // Classe de design pattern Singleton template <typename T> class SingletonSP { protected : // Pointeur vers l'instance unique static std::shared_ptr<T> singletonPtr; // Constructeur et destructeur privés pour éviter l'appel depuis l'extérieur. SingletonSP () {} ~SingletonSP() {} public : // Méthode statique pour accéder à l'instance unique du Singleton ; celle-ci est // construite si elle n'était pas encore allouée. static std::shared_ptr<T> getInstance() { if ( !singletonPtr ) singletonPtr = std::make_shared<T>(); return singletonPtr; } // kill() n'est a priori plus nécessaire, puisque singletonPtr sera détruit à la fin du scope // global, désalouant automatiquement l'objet pointé. }; // la définition du singleton statique, non initailisée car encapsulée. template <typename T> std::shared_ptr<T> Singleton<T>::singletonPtr; // Classe test utilisant la classe ci-dessus class Test : public SingletonSP<Test> { friend class Singleton<Test>; friend std::shared_ptr<Test> std::make_shared<Test>(); // friend pour avoir accès au constructeur friend class std::shared_ptr<Test>; // friend pour avoir accès au destructeur private : // Constructeur et destructeur private pour éviter l'instanciation/destruction Test() {} ~Test(){} // Constructeur de copie et opérateur d'affectation interdits Test(const Test& other) = delete; Test operator=(const Test& other) = delete; public : // une méthode quelconque void doSomething() { std::cout << "doSomething" << std::endl; } }; #endif // ndef DEFINE_SINGLETON
Et là, la belle erreur de compilation, qui en clair me dit qu'à la ligne 9 je fais appel aux constructeur et destructeur de Test, ce qui est interdit car ils sont private.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8 #include <iostream> #include "SingletonSP.hpp" int main() { Test::getInstance()->doSomething(); // <-ligne 9 return 0; };
L'erreur exacte du compilo est, sans toutes les inclusions de la stl (que j'ai remplacées par (...) pour rendre le truc lisible) :
(...)
SingletonSP.hpp:28:62: required from ‘static std::shared_ptr<_Tp1> SingletonSP<T>::getInstance() [with T = Test]’
main.cpp:6:9: required from here
SingletonSP.hpp:52:3: error: ‘Test::Test()’ is private
Test() {}
(...)
main.cpp:9:2: required from here
SingletonSP.hpp:53:3: error: ‘Test::~Test()’ is private
~Test(){}
(...)
Voilà, j'imagine qu'il s'agit d'une subtilité dans l'utilisation des shared_ptr que je ne vois pas, mais justement je ne la vois pas...
Précision, j'utilise g++ 4.8.1 avec les options -Wall, -Wextra et -std=c++11
Merci d'avance,
whityranger
Partager