Salut,
pour moi, le singleton ne s'implémente pas comme ça. Pour moi l'idée, c'est:
- le constructeur devient privé, on ne peut pas l'appeler en dehors de
la classe.
- une méthode public ( GetInstance() ) est ajouté à la classe et un
membre privée est ajouté lui aussi qui est un pointeur vers une instance.
Exemple:
class ClassA {
private:
ClassA * instanceA;
ClassA(...);
public:
getInstance()
};
ClassA::ClassA(...)
{
instanceA = NULL;
...
}
ClassA::getInstance()
{
if (instanceA == NULL) {
instanceA = new InstanceA();
}
assert(instanceA != NULL);
return instanceA;
}
Si on utilise ça sur la classe Font, c'est un peu différent puisqu'on a
plusieurs instances différentes.
L'accès à small_font devient par exemple:
Font::SmallFont();
On peut alors faire un
Font::SmallFont()->Render(...);
Ceci permet de garder exactement les mêmes #include qu'avant et évite de
créer un fichier global. De plus, je trouve ça plus propre perso.
Matt
Victor Stinner a écrit :
Salut,
*** Avant toute chose : mettez à jour votre dépôt SubVersion local, j'ai
changé un paquet de fichiers !!! ***
*** Email un peu long mais c'est pour expliquer en profondeur un problème de
Wormux vieux de 4 ans au moins ***
J'ai enfin fait le grand pas : j'ai écrit la classe qui va gérer des
singletons. En deux mots, c'est des instances qui vont être créer lorsqu'on y
accède. L'avantage est qu'on contrôle la création de l'instance, et qu'on
peut ajouter des bouts de code (ex: vérifier que SDL est lancé avant de
charger une image!). D'ailleurs j'ai trouvé une erreur: ImagesParSeconde
instanciait un objet Text qui a besoin d'une Font qui était créée ... avant
l'initilisation de SDL et TTF !!! (mais apparement, ça marchait quand
même :-P).
J'ai donc supprimé toutes les variables globales big_font, small_font, etc. et
j'ai remplacé ça par : global().small_font() (il faut un #include
"../include/global.h").
Le désavantage est que ça ajoute deux déréférencement de pointeur et deux if
pour chaque accès aux variables (un if pour être exact, l'autre est une
assertion => désactivée en mode non debug). Bon, je pense que ça coûte rien
du tout au niveau des performances et vous verrez après (dans mon email) tous
les avantages.
L'autre soucis est que si on met tout ce qui doit être instancié dans
"Global", la compilation d'un seul fichier demandera l'ensemble des fichiers
d'entêtes (.h) ... ce qui risque d'être plutôt lourdingue (ralentir la
compilation quoi). Une solution plus soft serait de faire une classe "Global"
pour chaque type d'objet (ex: Font, Graphic, Game, ... enfin, il faudrait en
discuter).
À terme, la majorité des variables globales devraient utiliser des singletons
(je pense). Par contre, pour certains classes "critiques", c'est peu/pas
envisageable. Je pense notamment à Config qui est utilisé à peu près partout
dans le jeu ...
---
Quelques problèmes résolus avec les singletons :
- Le problème des dépendences sont supprimés. Je m'explique : avant, tout
était en variable globale. Le gros problème est que les objets sont
instanciés l'un après l'autre. Mais que se passe-t-il lorsque A utilise B
alors que B n'est pas encore instancié ? Ben le compilateur ne dit rien, et
souvent ça passe. Mais ça arrive que ça plante ! Exemple : en modifiant
Config::Config() pour ajouter ttf_filename, j'ai utilisé DEFAULT_DATADIR.
Mais ... ça plantait lamentablement du côté de std::string !? Après une bonne
heure j'ai compris le problème : DEFAULT_DATADIR n'existait pas encore !
Gloups ! J'espère que vous voyez le problème. J'ai rencontré ce problème très
très souvent. Un autre au hasard : Time::Time() accédé avant sa création
(renvoyait n'importe quoi donc).
- On contrôle enfin quand l'objet est détruit. Avant, les bibliothèques
étaient désactivées (SDL_Quit & Cie, et avant l'équivalent pour ClanLib), et
seulement après les objets étaient détruits. Le problème est que la
destruction a besoin de la bibliothèque (ex: l'informer de la destruction de
l'objet). Et fatalement : ça plante. J'ai eu ce problème très récement :
Font::~Font() qui appelait TTF_CloseFont() *après* TTF_Quit(). (*bug
corrigé*)
Hum, il doit y avoir d'autres avantages, mais je ne vois pas trop là tout de
suite.
J'espère que je vous ai convaincu de l'utilité de mon dernier patch de 1000
lignes :-P
Victor