Besoin :
Certaines classes nécessitent une initialisation ou une désinitialisation particulières qui ne doivent intervenir qu'UNE seule fois dans la vie d'un programme. Cela indépendament du nombre de fois que la classe est instanciée!
Objectif :
Définir une classe de base qui se caractérise par l'appel d'un constructeur et d'un destructeur spécifiques respectivement au premier appel et au dernier appel lors de l'execution d'un programme.
Exemple :
Concrètement, prennons par exemple une classe qui encapsule la gestion d'un Socket sous windows. Il est nécéssaire dans le constructeur d'effectuer une initialisation de la librairie WinSock2. Et dans le destructeur, il est nécessaire de relacher cette même librairie.
Cette classe pourrait donc ressembler à ceci :
Code :
- class CSocket // rien à voir avec MFC !
- {
- public:
- CSocket() {
- WSADATA wsaData;
- WSAStartup(MAKEWORD(2,2),&wsaData); }
- ~CSocket() {
- WSACleanup(); }
- }
|
Le problème de cette classe et qu'à chaque intanciation, le constructeur (et ensuite le destructeur) sont appellés systématiquement multipliant l'initialisation et la libération de la librairie WinSock. Dans ce cas précis, cela ne pose aucun problème (la librairie WinSock s'en accomode parfaitement).
Proposition :
Créer une classe de base qui execute les fonctions membres "PremierConstructeur" et "Dernier Constructeur" respectivement lors du premier appel du constructeur et lors du dernier appel du destructeur. Faire hériter de cette classe de base toutes les classes tel que CSocket.
Cette classe de base pourrait ressembler à ceci :
Code :
- class Cinit
- {
- public:
- Cinit() {
- if( cpt++ == 0 )
- PremierConstructeur(); }
- ~Cinit() {
- if( --cpt == 0 )
- DernierDestructeur(); }
- virtual void PremierConstructeur() = 0;
- virtual void DernierDestructeur() = 0;
- private:
- static int cpt;
- }
|
Ne pas oublier dans le code d'ajouter :
Code :
- int Cinit::cpt = 0;
|
Retour à l'exemple :
Revenons à notre classe CSocket :
Code :
- class CSocket : public Cinit
- {
- public:
- CSocket();
- ~CSocket();
- private:
- void PremierConstructeur() {
- WSADATA wsaData;
- WSAStartup(MAKEWORD(2,2),&wsaData); }
- void DernierDestructeur() {
- WSACleanup(); }
- }
|
La suite... :
Maintenant, ce mécanisme est loin d'être parfait et en fait tres peu exploitable. Au mieux, c'est le point de départ (en espérant etre parti dans la bonne direction).
Les problèmes sont les suivants :
1) Cette classe ne gère l'initialisation que d'une seule classe dériviée. On ne peut pas s'en servir pour initiliser plusieurs classes dériviées différentes. Il faut mêtre en cause le compteur qui est unique et propre à la classe Cinit alors qu'il faudrait l'intégrer dans la classe dérivée. Problème, je voudrais éviter au maximum d'altérer les classes dériver et rendre ce mécanisme transparent !
2) Le premier constructeur et le dernier destructeur ne peuvent être exploités que par la classe dérivée. Les classes dérivées des dérivées n'en profitent pas. Cela n'est pas un réel problème, il suffit de faire hériter de Cinit l'ensemble des classes, mais dans ce cas on retombe dans le problème n°1.
J'entrevois peut etre une solution qui consisterait à détecter quelle classe appelle les fonctions membres virtuelles PremierC et DernierD et de gérer automatiquement un compteur static par type de classe (une list:: qui compte/décompte séparement pour chaque classe dérivant de Cinit).
Quelqu'un à une meilleure idée d'implémentation. Aujourd'hui, j'ai plusieurs classes vraiment différentes qui doivent implémenter ce type d'initilisation/désinitialisation et j'implémente le mécanisme dans chaque classe. Ca fait du code à dupliquer....pour rien.
Merci d'avance.
Xter.
Message édité par xterminhate le 01-02-2004 à 20:26:38