classes interdépendantes : déclarer une classe sans la définir

Le
meow
J'ai deux classes, A et B, chacune dépend de sous parties de
l'autre et j'aimerai si possible éviter de multiplier mes arguments
templates. Y a t'il un moyen d'écrire quelque chose du genre :

class A; //! t'inquiètes pas copain compilo, la définition vient plus
loin
class B{
// j'utilise des types définis dans A
};
class A { // tu vois j'ai pas menti !
// j'utilise des types définis dans B
}

Bon, ok, ça a pas l'air bien joli niveau design Alors si en sus
vous aviez des conseils pour éviter ce genre d'horreur. Je vous
explique un peu mieux mon problème, histoire de pas parler dans le
vide :

Ma classe A est une structure de donnée qui rend compte d'un objet
volumique (une triangulation 3D, donc un ensemble de tetraedres,
triangles, arretes et sommets avec les relations d'incidences qui vont
bien)
Ma classe B est une structure de donnée qui rend compte d'un objet
surfacique (demi arete, pour qui connait (merci Mr Kettner) )
j'ai un objet a de type A et je veux construire son bord sous la forme
d'un objet b de type B. Pour cette construction, je vais avoir besoin
de stocker les "échafaudages" de b dans a. Donc A doit connaitre B (ou
du moins certaines sous parties de A doivent connaitre certaines sous
parties de B)
Mais je veux aussi que les sommets de b gardent l'information de leur
origine dans a (grosso modo je veux que les sommets de b pointent sur
les sommets de a dont ils sont issus) moralité B doit aussi
connaitre A.
Je fais comment ? :D

Ce a quoi j'avais pensé partant de l'observation qu'il était très laid
d'avoir un objet qui contienne les "échafaudages" d'un autre, c'était
d'avoir deux classes très proches : A et AA, la première vierge de
toute pernition de B, et la seconde qui ne serait utilisée que le
temps de la construction d'un objet de type B. A ne "connaitrait"
ainsi personne, B connaitrait A et AA connaitrait à la fois A et B.
Un objet aa de type AA serait initialisée par copie d'un objet de
classe A dont elle stockerait les références dont on a besoin dans les
éléments de la classe B, et stockerait les "échafaudages" de b avant
d'etre détruit, une fois b construit.
Je pense que ce qui m'a rebuté c'est le fait que ma classe A est très
grosse et que ça me gène de "gaspiller" de la place mémoire avec deux
objets quasi identiques en mémoire.
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses Page 1 / 3
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
AG
Le #308458
class A; //! t'inquiètes pas copain compilo, la définition vient
plusloin
class B{
// j'utilise des types définis dans A
};
class A { // tu vois j'ai pas menti !
// j'utilise des types définis dans B
}

Bon, ok, ça a pas l'air bien joli niveau design... Alors si en sus
vous aviez des conseils pour éviter ce genre d'horreur. Je vous
explique un peu mieux mon problème, histoire de pas parler dans le
vide :


Je pense que c'est la manière traditionnelle de faire. Aussi laide
soit-elle.

Sylvain
Le #308457
meow wrote on 29/06/2007 17:39:
J'ai deux classes, A et B, chacune dépend de sous parties de
l'autre... et j'aimerai si possible éviter de multiplier mes arguments
templates. Y a t'il un moyen d'écrire quelque chose du genre :

class A;
class B{
// j'utilise des types définis dans A
^^^^^ plus sûrement des méthodes / propriétés.

};
class A { // tu vois j'ai pas menti !
// j'utilise des types définis dans B
^^^^^ idem

}

Bon, ok, ça a pas l'air bien joli niveau design... Alors si en sus
vous aviez des conseils pour éviter ce genre d'horreur.


une déclaration avec définition différée n'a rien de très laid; si de
plus les classes si définies conjointement, la relecture n'est pas gênée
par cela.

[...]
Je pense que ce qui m'a rebuté c'est le fait que ma classe A est très
grosse et que ça me gène de "gaspiller" de la place mémoire avec deux
objets quasi identiques en mémoire.


une alternative à votre approche en, est effet, de ne déclarer qu'une
seule et unique classe qui contienne toutes les informations nécessaires
à la construction des 2 ensembles de données avec 2 jeux de méthodes
d'utilisation selon le besoin externe; on pourrait pour faciliter cet
usage définir 2 classes de "travail pur", entièrement initialisée à
partir de la première et non à même de générer elle même les points /
grandeurs caractéristiques (pour ne pas dupliquer cette partie qui est
le corps de votre interdépendance actuelle).

Sylvain.

meow
Le #308429
On doit la se comprendre... En fait j'ai deux problèmes, le premier
technique, et le second plutot design.
Commençons par le premier :

class A;

class B{
A a;
public:
B(A aa):a(aa){}
A& get () {return a;}
};

class A{
int i;
public:
A(int I):i(I){}
int get () {return i;}
};

int main () {
B b(A(15));
std::cout<<b.get().get()<<std::endl;
}

ne compile pas :

/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp:6:
error: field 'a' has incomplete type
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp: In
constructor 'B::B(A)':
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp:8:
error: 'aa' has incomplete type
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp:3:
error: forward declaration of 'struct A'
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp:8:
error: class 'B' does not have any field named 'a'
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp: In
constructor 'B::B(A)':
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp:8:
error: 'aa' has incomplete type
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp:3:
error: forward declaration of 'struct A'
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp: In
constructor 'B::B(A)':
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp:8:
error: 'aa' has incomplete type
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp:3:
error: forward declaration of 'struct A'
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp: In
member function 'A& B::get()':
/home/schwarz/Bacasable/workspace/Exemples/Class_forward_def.cpp:9:
error: 'a' was not declared in this scope

d'où ma première question : comment on fait ce genre de choses ?
Fabien LE LEZ
Le #308428
On Mon, 02 Jul 2007 01:37:29 -0700, meow
B(A aa):a(aa){}


On passe généralement les arguments par référence constante (sauf pour
les types de base, comme int et les pointeurs).

B (A const& a_) : a (a_) {}

Par ailleurs, ton B contient un A. Ce qui signifie que A doit être
définie avant B :

class B;

class A
{
// A peut contenir des B&, des B*, mais pas de B
};

class B
{
// B peut contenir un A.
};

Si tu définis les contenus avant les contenants, tu n'auras aucun
souci.

Tu noteras que si un B contient un A, un A ne peut pas contenir de B.

Jean-Marc Bourguet
Le #308427
meow
On doit la se comprendre... En fait j'ai deux problèmes, le premier
technique, et le second plutot design.
Commençons par le premier :

class A;

class B{
A a;
public:
B(A aa):a(aa){}
A& get () {return a;}
};

class A{
int i;
public:
A(int I):i(I){}
int get () {return i;}
};

int main () {
B b(A(15));
std::cout<<b.get().get()<<std::endl;
}

ne compile pas :


Pour avoir un membre d'un type donne, il faut avoir vu la definition (et
pas uniquement la declaration) du type. Ici quand tu definis B, tu n'as vu
qu'une declaration de A. (Un exemple de problemes de l'absence de
definition: comment calculer la taille de B si on ne connait pas celle de A?)

La declaration de la destination suffit pour des pointeurs et des
references, elle suffit aussi pour la declaration de fonctions ayant ce
type en parametre.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org

meow
Le #308426
La réponse est donc : "c'est pas possible dirrectement, il faut passer
par des pointeurs". Merci.
Merci aussi pour les remarques sur mon code, et oui, c'est pas parce
qu'on fait une exemple rapide qu'il faut le faire laidement ;)
meow
Le #308425
Après quelques essais supplémentaires...

La declaration de la destination suffit pour des pointeurs et des
references, elle suffit aussi pour la declaration de fonctions ayant ce
type en parametre.


Et encore... Je n'ai visiblement pas le droit de faire grand chose
avec de tels pointeurs, ce qui en définitive me parrait logique.
J'explicite :

class A;

class B{
A *a;
public :
B(){a=new A();}
}

Rien que ça, ça va déjà poser problème parce qu'à ce moment le compilo
n'a aucun moyen de savoir si j'ai bien un tel constructeur dans A...

Tu noteras que si un B contient un A, un A ne peut pas contenir de B.


Bein oui... Et c'est bien ce qui me désole.
En fait j'ai l'impression que si le compilo mettait ces cas de figure
de coté jusqu'à trouver la définition de A il aurrait l'occasion de
faire les liens à ce moment, ce qui permettrait d'avoir des
définitions récursives... Mais j'imagine qu'il doit y avoir des soucis
que je ne vois pas :)

Concrètement maintenant, si je veux m'en sortir il semblerait qu'une
méthode pourrait consister dans l'emploi d'un héritage virtuel.
Genre une classe abstraite AA déclarant les fonctions virtuelles dont
j'aurai besoin dans B, une classe BB déclarant les fonctions dont
j'aurai besoni dans A, puis des classes A et B descendant
respectivement de AA et BB et contenant respectivement un BB* et un
AA*
...J'ai bon ?

AG
Le #308424
class A;

class B{
A *a;
public :
B();
};

class A
{
int a;
public:
A(){a=0;};
};

B::B(void)
{
a = new A();
}

chez moi ça marche.
Fabien LE LEZ
Le #308394
On Mon, 02 Jul 2007 04:26:48 -0700, meow
class A;

class B{
A *a;
public :
B(){a=new A();}
}


N'oublie pas le point-virgule à la fin de la définition de la classe !


class A;

class B
{
public:
B();
private:
A* a;
};

class A
{
...
};


B::B()
: a (0)
{
a= new A;
}

Fabien LE LEZ
Le #308393
On Mon, 02 Jul 2007 04:26:48 -0700, meow
Tu noteras que si un B contient un A, un A ne peut pas contenir de B.


Bein oui... Et c'est bien ce qui me désole.


Mais pour le coup, ça n'a rien à voir avec une restriction du
compilateur.

Si un B contient un A, il est forcément plus grand que le A.
Si un A contient un B, il est forcément plus grand que le B.
Du coup, si un A contient un B qui lui-même contient un A, l'objet A
est plus grand que lui-même.


Publicité
Poster une réponse
Anonyme