Salut,

Envoyé par
anubis_1001
Bonjour,
je reviens sur le sujet avec une question plus précise..
Supposons que j'ai la classe suivante:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
|
class A {
private:
B *b;
public:
A();
~A();
const B & getReferenceSurB() const; //est-ce le bon prototype pour récupérer la référence sur B?
}
A::A() { b = new B(); }
A::~A() { delete b; }
const B & getReferenceSurB() const { return *b; }
//.. dans main ensuite ..
int main() {
A a();
B b = a.getReferenceSurB(); // dois-je utiliser B &b a la place, et puis-je utiliser b par la suite?
} |
Je pense que vous avez compris ce que je veux faire, j'aimerais savoir si c'est le meilleur moyen de récupérer la référence sur B.
Merci d'avance pour votre aide

Le plus facile est peut être d'essayer, afin de voir ce que te dis le compilateur 
Plus sérieusement, il est tout à fait possible de déclarer b comme une variable "normale" ET de déclarer b comme étant une référence (obligatoirement constante) sur un objet de type B. La décision entre l'une ou l'autre solution dépendra essentiellement de ce que tu veux faire de b par la suite, et de quelques conditions qui devront être respectées (ce qui impliquera d'ailleurs certaines conséquences
) :
1 2 3
| B b = a.getReferenceSurB();
/* que l'on pourrait aussi écrire sous la forme de
B b(a.getReferenceSurB()); |
créera b comme étant une copie du membre A::b (en fait comme une copie de ce qui se trouve à l'adresse pointée par A::b, vu que A::b est un pointeur sur B dans ton exemple
)
Tu pourras donc manipuler b strictement à ta guise, en appelant, pourquoi pas, des fonctions non constantes ayant pour but de modifier b.
Cependant, comme tu manipule seulement une copie de ce qui est pointé par A::b, aucune des modifications que tu pourra apporter ne seront répercutées dans A::b.
De plus, cela implique que ta classe B soit copy-constructible et / ou assignable.
De plus, tu ne pourra pas utiliser b de manière polymorphe.
Le code
B const & b = a.getReferenceSurB();
est tout aussi valable, évite la copie de B (ce qui peut s'avérer intéressant) mais interdit toute modification de b, car il s'agit d'une référence constante (tu ne pourra donc invoquer que les fonctions constantes de B)
Par contre, si la classe B est une classe parent dans une hiérarchie de classe, tu pourras envisager d'invoquer les comportements (qui s'engagent à ne pas modifier l'objet, toujours
) polymorphes qu'elle propose.
Cela m'amène naturellement à parler de deux considérations connexes:
Il n'y a que rarement de raison de recourir à l'allocation dynamique pour les membres de classes, exception faite du cas où tu souhaites profiter du polymorphisme apporté par une hiérarchie de classe:
Un code proche de
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
| class B
{
/* what ever */
};
class C : public B
{
/* what ever */
};
class D : public B
{
/* what ever */
};
class E : public B
{
/* what ever */
};
class A
{
public:
A(int i)
{
switch(i)
{
case 1:
b = new C;
break;
case 2 :
b = new D;
break;
case 3 :
b = new E;
break;
}
}
private:
B * b;
}; |
pourrait se justifier dans certains cas, mais uniquement si, comme le code l'indique, B est une classe parent dans une hiérarchie de classe
(il n'y a pas vraiment de raison d'allouer dynamiquement b si ce n'est pas le cas car le membre aura, de toutes manières, exactement la durée de vie de l'instance de A dans laquelle il se trouve
)
La deuxième considération connexe a trait à l'accesseur que tu fournis...
Il existe, en effet, une règle de conception qui s'appelle la "loi demeter".
Cette loi nous explique que, si la class A utilise en interne une instance de la classe B, l'utilisateur de la classe A (ex: la fonction main ou toute fonction membre d'une classe C qui manipulerait une instance de A) ne devrait pas avoir à connaitre la classe B afin de pouvoir utiliser A:
Soit A utilise b exclusivement en interne, et c'est alors à A de fournir les comportement qui manipulent b, sans que ce qui manipule A ne sache que b est utilisé, soit b est une instance particulière de la classe, manipulée par A mais gérée par ailleurs.
Dans le meilleur des cas A ne devrait fournir que l'information permettant d'identifier b de manière unique de manière à ce que la fonction qui passe par A pour retrouver b puisse demander à la classe qui a en charge la gestion des B (la "collection de B's") l'instance correspondante afin de la manipuler 
De cette manière, tu arriverais à clairement séparer les responsabilités et tu pourrais donc éviter d'avoir un code proche de
a.getB().getC().getD().doSomething();
qui t'oblige à connaitre B, C et D en plus de A alors que... tu travailles avec A (et qui augmente les dépendances de manière catastrophiques
)
Partager