[C++] Jumelage de classes, est-ce possible?

Bonsoir les geeks,

J’ai des connaissances restreintes en C++, et ça fait un moment que je sèche. Vu qu’il doit y avoir quelques pros dans le coin, je me suis dit qu’une âme charitable aurait peut-être une solution toute simple.

Voilà mon problème: j’ai une hiérarchie de classes, une première classe person, et une classe qui en dérive, nommée employee, chacune avec un membre private accessible en lecture/écriture via des méthodes Get/Set, tout ce qu’il y a de plus banal. En parallèle, et c’est pourquoi j’ai intitulé le sujet “jumelage de classes”, je souhaiterais créer des classes de conteneurs (ici des vector de la STL) pour stocker des objets de ces deux types. Et, là aussi, je souhaiterais que la classe de conteneurs pour les objets employee dérive de la classe de conteneurs des objets person, ceci afin de ne pas avoir à réécrire une méthode qui est commune aux deux conteneurs (en l’occurence, la méthode ClearAllAges()). Seule contrainte que je fixe: pas de template.

Hop, le code du header:

[code]#include <vector.h>

//Deux classes, dont l’une dérive de l’autre
class person{
public:
int Age(void) {return _age;}
void SetAge(int value) {_age = value;}
private:
int _age;
};

class employee : public person{
public:
int ID(void) {return _ID;}
void SetID(int value) {_ID = value;}
private:
int _ID;
};

//Les classes de conteneurs correspondantes. Je souhaiterais que, là aussi, la deuxième
//soit dérivée de la première, afin de ne pas avoir à réécrire la méthode ClearAllAges
//qui est en fait commune.
class persons : public vector{
public:
int ClearAllAges(void){
for(int i=0; i < this->size(); i++)
this->operator.SetAge(0);
}
};

class employees : public vector{
public:
int ClearAllAges(void){ //<---- comment éviter ce doublon?
for(int i=0; i < this->size(); i++)
this->operator.SetAge(0);
}
int ClearAllIDs(void){
for(int i=0; i < this->size(); i++)
this->operator.SetID(0);
}
};[/code]

Le code ci-dessus fonctionne, mais au prix d’un doublon, dû au fait que les deux classes sont dérivées de vector. Mon but serait donc de dériver employees de persons, plutôt que de vector, afin d’économiser la méthode employees::ClearAllAges(). Comment dois-je m’y prendre? Ou est-ce tout simplement impossible?

Merci d’avance à tous ceux qui m’apporteront leurs précieux conseils!

Je pense que je n’ai pas bien compris car pour moi ce que tu veux faire c’est

[code]class persons : public vector{
public:
int ClearAllAges(void){
for(int i=0; i < this->size(); i++)
this->operator.SetAge(0);
}
};

class employees : public persons{
public:
int ClearAllIDs(void){
for(int i=0; i < vector::size(); i++)
vector::operator.SetID(0);
}
};[/code]

Et je ne vois pas où est le problème…

Moloch : ben dans ton exemple, la methode setId va planter, car elle est defini dans la classe employee, et pas dans la classe personne…

Ah j’avais pas lu, dans ce cas là ça veut dire qu’il faut descendre ID à person.

Justement, mais ID doit impérativement rester dans employee. Surtout qu’après je veux rajouter une autre classe, disons boss, dérivée de person, et qui ne doit surtout pas avoir un _ID, mais un nouveau champ _income par exemple. Et bien sûr, parallèlement à cette nouvelle classe boss, il me faut un conteneur associé, de la même manière que employees est associé à employee.

Merci quand même pour la proposition. Plus j’y pense, cependant, et plus je me demande si ce n’est tout simplement pas impossible…

Oui parce que ton architecture est tout simplement mauvaise (sans animosité aucune).

Pourquoi Boss ne doit pas avoid _ID ? En quoi c’est un problème ? Tu peux redéfinir le set/get d’ID pour que ça retourne une valeur fixe, par exemple “BOSS_ID”, idem pour personne tu mets “NO_ID” (je te conseille de faire un objet ID).

je vois

[code]class CID
{

private:
long m_lID;

public:
CID(long lID) { (*this) = lID; }

public:
operator long() const { return m_lID; }
CID &operator = (long lID) { m_lID = lID; }

public:
bool operator == (const CID &ID) const { return this->m_lID == ID.m_lID; }
bool operator != (const CID &ID) const {return !(*this == ID); }
};

enum EDefaultID
{
TypePerson = 0,
TypeBoss = 1
}

class CPerson
{
private:
CID m_ID;

protected:
CPerson(long lID) { m_ID = lID; }

public:
CPerson() { m_ID = TypePerson; … }

public:
const CID &GetID() const { return m_ID; }
CID &GetID() { return m_ID; }
};

class CEmployee : public CPerson
{

public:
CEmployee(CID &ID) : CPerson(ID) { … }

}

class CBoss : public CEmployee
{

public:
CBoss() : CEmployee(TypeBoss ) { … }

}[/code]

J’ai pas de compilo installé sous la main pour vérifier, mais t’as l’idée de base… Si Boss n’a besoin de rien, ne le fait pas dériver d’employee mais de person, parfois la logique objet ne colle pas à la réalité.

Ca c’est fort possible effectivement, et j’apprécie ta franchise. Je ne serais pas contre le fait que boss possède aussi un ID… si mes classes étaient aussi peu nombreuses que dans l’exemple person-employee-boss. Oui, parce que des classes, en fait, j’en ai bien plus que ça, une cinquantaine pour être exact, et liées entre elles de la même façon que person-employee-boss. En fait, je les ai rangées en arbre, afin que celles partageant les mêmes propriétés soient dérivées d’une même classe qui réunit les propriétés communes (je sais pas si c’est très clair). Peut-être bien que j’ai tout faux du point de vue design, mais c’est ce qui me semblait le plus logique pour avoir le moins de redondances possible, tant du point de vue du code à écrire que du code produit à la compilation. C’est d’ailleurs pourquoi je souhaiterais éviter les templates, vu que je peux très bien m’en sortir en les utilisant (j’ai déjà essayé, ça marche, et je garde d’ailleurs cette solution de secours), mais au prix d’un exe un peu lourd.

Parce que, il me semble que (dites-moi si je n’ai définitivement rien compris), lorsqu’une méthode est compilée, le code machine correspondant n’est généré qu’une fois, et est commun à toutes ses éventuelles classes dérivées. Et si l’on fait appel aux templates, à chaque type différent est compilée une copie de la méthode template, ceci pour autant de types que nécessaire, augmentant la taille de l’exe.

En tous cas, merci pour ton code, je vais le regarder attentivement et je t’en dirai des nouvelles. Mais mon but, c’est de rester le plus simple possible. Je n’ai pas encore étudié suffisamment ta proposition, il faut voir si c’est gérable avec mon énorme arbre de classes, ou si je risque de me retrouver avec une trop grande quantité de méthodes en plus.

Oui, le code est inliné. Est-ce vraiment un problème ?

Impossible à dire… Je me doutais bien que ton problème d’ID n’était pas là pour faire joli, mais si tu as besoin de généricité cela signifie que tes objets doivent avoir quelque chose en commun, donc à voir. Quand tu arrives à quelque chose de très difficile à faire techniquement (sur le plan language pur) c’est en général un problème de conception.

Au pire tu peux poster un schéma de tes classes. :stuck_out_tongue:

C’est une bonne idée, mais vu que je n’ai pas le diagramme (papier) sous la main pour le moment. Je ferais une photo dans quelques jours, et je mettrai à jour.

Pour un diagramme de classe, passe un coup de doxygen, il te les génerera au poil surtout si tu lui ajoutes un machin qui s’appelle dot, ça génère tout plein de grafes de dépendances super pratique. Le must c’est quand tu commentes avec une certaine syntaxe, de la doc à pas cher.

Oui c’est pas mal Doxygen et ça te pousse à bien documenter tes fonctions.

Ouaip, je vais regarder pour Doxygen, merci du tuyau. Je mettrai quand même une photo du diagramme dans un premier temps, je ne l’ai pas sous la main avant demain ou après-demain, mais il est terminé. Ca sera donc plus vite fait que si je dois encore me plonger dans la doc de Doxygen. J’up dès que possible.

Bonjour,

Sans rentrer dans le détail, ton soucis essentiel :
c’est que si B hérite de A … malheureusement Collection n’herite pas de Collection

C’est un probleme rencontré par tout ceux qui ont travaillé sur des sauvegarde/base de donnée Objet.

[quote=“Zoulou, post:13, topic: 28144”]Bonjour,

Sans rentrer dans le détail, ton soucis essentiel :
c’est que si B hérite de A … malheureusement Collection n’herite pas de Collection

C’est un probleme rencontré par tout ceux qui ont travaillé sur des sauvegarde/base de donnée Objet.[/quote]

Parfaitement, c’est exactement ça. La question étant maintenant, vu que le problème semble connu: existe-t-il une solution simple (j’en ai déjà une avec les templates, mais c’est la solution de secours)?

Hors Sujet: quand j’y pense ca serait tellement simple à faire en C# 2.0; j’allais meme te proposer une solution, mais apres j’ai vu que c’était du C++ :/.

tiens percil : poste quand meme la solution en C#, ca m’interesse :stuck_out_tongue:

Bah en C#:

Vu que Employee est dérivée de Person, elle rentre sans problème dans la liste. Ensuite pour les parcours de la liste:

[code]// liste de toutes les personnes
foreach(Person Item in LaListe)
Console.WriteLine(Item.Name);

// liste seulement les employés
foreach(Employee Item in LaListe)
Console.WriteLine(Employee.Job.Name);[/code]

Exemple très basique, mais qui illustre bien la puissance du langage. Edit que j’ai oublié un bout (clearAllAges):

foreach(Person Item in LaListe) Item.ClearAge();

Il existe aussi une autre solution à base de Delegate (LaListe.ForEach(…)) que je n’ai pas trop l’habitude d’utiliser. Enfin ma méthode est un peu différente vu qu’elle ne tient compte que d’un conteneur. Il est aussi possible de faire un conteneur qui contient une liste, qui implémente clearAllAges(), de faire un container dérivé du container principal qui appelle pour chaque élément du container la méthode clearAge() du parent.

oO putain ca tue ! je savais pas que C# savais faire ca !
Effectivement, le foreach prends tout son sens ici.
Faut vraiment que je m’achete un bouquin de C# pour arreter de faire du java/c++ en C# moi …

merci pour le tuyau ! (et merci a glop au passage pour les coups de main qu’il m’a deja filer ^^)

Edit : Du coup, pour son truc, suffirai de faire une classe Clist, qui derive de List, et de definir dans cette classe les methode clearallId, clearallAge, et de faire des foreach sur this ? c’est ca?

Personnellement je verrais plutot une classe contenant une List plutot qu’une dérivée (GloP m’avait dit que dériver les collections génériques c’était le mal).

[quote]Cil’ date=’ 28 Mar 2006, 15:06’ post=‹ 457719 ›]
Personnellement je verrais plutot une classe contenant une List plutot qu’une dérivée (GloP m’avait dit que dériver les collections génériques c’était le mal).[/quote]

Ok, c’est noté :stuck_out_tongue: