OVH Cloud OVH Cloud

Acceder a un membre donnée d'une autre classe

16 réponses
Avatar
Pasletot
Bonjour,

Débutant C++ circonspect.

Hypothèse : obtenir en retour de « a.foo() » et de « b.bar(« ) une seule valeur de « m_x ».

Valeurs obtenues en retour de a.foo() et de b.bar() : m_x = 55 ; et
m_x = 88 .

Le programme sous forme condensée.

int main()
{
A a(55); // m_x
B b(88); // m_a
a.foo();
b.bar();
return 0;
}

// Classe A // attribut « int m_x ; »

A::A(int x): m_x(x) {} // x = 55

int A::foo() const

{ return m_x; }

// Classe B // attribut « A m_a ; »

B::B(int z) : m_a(z) {} // z = 88

int B::bar() const

{ return m_a.foo();}

Résultats partiels obtenus sur console .

A a ; // Création objet A
Constructeur de m_x ; // oui
Valeur de « m_x » = 55 ;
Adresse de m_x et de « a » 0x28FF0C

B b ; // Création objet B
Constructeur de m_x ; // ??
Valeur de « m_x » = 88
Adresse de m_x et de « b » 0x28FF08
Constructeur de m_a
Adresse de m_a dans B ::B() 0x28FF08

La réinitialisation de « m_x » sous forme de copie de « m_x » lors de l’appel de « B b » explique (pour moi) les résultats obtenus, y compris lorsque « m_x » est initialisé par int « m_x(int) » (retours de la même valeur).
Par contre l’appel du constructeur de « A » par « B b() » et la réinitialisation d’une « m_x » à la valeur de « m_a » me désorientent.

Pouvez-vous m’apporter des éléments de réponse sur la raison pour laquelle « B b » appelle le constructeur de « A » et réinitialise « m_x » lorsque c’est possible ( « x » non int) ?

Merci

6 réponses

1 2
Avatar
Olivier Miakinen
Le 08/07/2017 08:48, pasletot m'a répondu :
ça prend un tour curieux. Non tes questions ne me semblent pas incongrues. En ce
qui me concerne j'ai recherché un site avec forum sur le C++ ouvert à tout
intervenant qui s'inscrit, sans plus.
J'accède au net avec firefox, utilise google pour accéder à l'adresse ci-dessous
https://www.generation-nt.com/reponses/acceder-membre-donnee-039-autre-classe-entraide-4271063.html

Ok. Merci pour cette réponse très claire.
et une fois connecté j'utilise l'éditeur "postez une réponse " qui est affiché
au nom de mon avatar, réponse qui doit être validée avant parution.

En bas à droite de chacune des réponses que tu peux lire, il y a un
bouton « Répondre en citant » qui devrait correspondre à ce dont je
parlais. Je viens de l'essayer, mais comme je n'étais pas encore inscrit
il va peut-être falloir du temps avant que ma réponse n'apparaisse. Et
malheureusement ça ne semble pas permettre d'éditer la citation, mais
c'est peut-être là encore parce que je n'étais pas inscrit au moment
d'essayer.
Est-ce-que ça répond à ta question ?

Oui, parfaitement.
J'espère ne pas arriver à user ta patience.

J'en ai beaucoup. ;-)
--
Olivier Miakinen
Avatar
Olivier Miakinen
Le 08/07/2017 09:35, pasletot a écrit :
Entendons nous bien sur le fond des éclaircissements que je souhaiterais.
J'ai cru comprendre qu'en C++ lors de sa création un objet accède d'emblée à son
constructeur.
Pourquoi dans mon exemple "B b" accède d'emblée à celui de la classe "A" ?

L'écriture que tu as donnée était la suivante :
B::B(int z) : m_a(z) {}
Je me demande si dans ce cas-là on ne commence pas par créer un m_a (de
de type A) *avant* d'appeler le constructeur de B en lui passant le m_a
déjà créé.
Cela dit, je ne connais pas bien les détails d'implémentation (qui bien
souvent ne sont pas importants même si parfois ils peuvent l'être) alors
je laisse les gourous répondre s'ils nous lisent.
De même j'ai cru comprendre que les données membres privées d'une classe étaient
accessibles par les méthodes de cette classe.

Oui. En l'occurrence, les données membres privées d'une *instance* de
classe sont accessibles par les méthodes de cette *instance* de classe.
Pourquoi dans mon exemple l'accession à la seule donnée membre de la classe "A"
donne en retour, selon le cas ou selon les modalités d'accès , deux résultats
différents. ?

Dans ton exemple, j'ai cru comprendre que tu n'as aucun attribut « de
classe » (en C++ ils sont déclarés avec le mot-clé static) mais que des
attributs « d'instance ». Vu que tu crées des instances différentes de
ces classes, il est normal que leurs valeurs soient différentes.
Voici un pointeur pris un peu au hasard parmi les résultats d'une
recherche Google :
http://frog.isima.fr/antoine/base.shtml#attributclasse
(si je trouve mieux je l'enverrai ensuite).
Soit dit en passant, Lucas t'avait déjà répondu en disant que les
classes (A et B) sont comme des types (int, char, float) et que tu
peux en avoir de nombreux avec des valeurs différentes.
D'ailleurs je reprends ton exemple :
int main()
{
A a(55); // m_x
B b(88); // m_a
a.foo();
b.bar();
return 0;
}
Remplaçons-le par :
int main()
{
int a(55);
int b(88);
a;
b;
return 0;
}
Es-tu surpris que b ait une valeur différente de celle de a bien que
tous les deux soient du même type int ? Eh bien c'est pareil avec ton
a qui était de type A et ton b.m_a qui était aussi de type A : même
type, mais valeurs différentes.
--
Olivier Miakinen
Avatar
Olivier Miakinen
Le 08/07/2017 21:45, je répondais à pasletot :
J'ai cru comprendre qu'en C++ lors de sa création un objet accède d'emblée à son
constructeur.
Pourquoi dans mon exemple "B b" accède d'emblée à celui de la classe "A" ?

L'écriture que tu as donnée était la suivante :
B::B(int z) : m_a(z) {}
Je me demande si dans ce cas-là on ne commence pas par créer un m_a (de
de type A) *avant* d'appeler le constructeur de B en lui passant le m_a
déjà créé.

Je pense que, pour que le constructeur de A soit appelé par le
constructeur de B et non avant, il faudrait l'écrire comme ça :
B::B(int z) { m_a(z); }
Cela dit, je ne programme pas souvent en C++ et je peux me tromper.
--
Olivier Miakinen
Avatar
espie
In article <ojrcns$30e1$,
Olivier Miakinen <om+ wrote:
Le 08/07/2017 09:35, pasletot a écrit :
Entendons nous bien sur le fond des éclaircissements que je souhaiterais.
J'ai cru comprendre qu'en C++ lors de sa création un objet accède

d'emblée à son
constructeur.
Pourquoi dans mon exemple "B b" accède d'emblée à celui de la classe "A" ?

L'écriture que tu as donnée était la suivante :
B::B(int z) : m_a(z) {}
Je me demande si dans ce cas-là on ne commence pas par créer un m_a (de
de type A) *avant* d'appeler le constructeur de B en lui passant le m_a
déjà créé.

Ca ne veut a peu pres rien dire.
Prenons un objet quelconque.
class B {
A a;
public:
B();
};
la construction de B c'est essentiellement:
- savoir ou on va construire un B (il faut la place)
- construire l'objet, ce qui veut dire
- construire chacun de ces champs, dans l'ordre de declaration de la classe.
- puis l'objet lui-meme
Voyons ca en terme de constructeur. Tu peux tres bien avoir par exemple
class A {
int f;
public:
A(int i = 5) { f = i; }
A& operator=(int i) { f = i; return *this;}
};
Si maintenant le constructeur par defaut de B ressemble a:
B::B() {}
ben l'element a sera construit avec son constructeur par defaut, puis on
finis de construire B en executant le code (vide) entre accolades.
Si maintenant tu as
B::B() { a = 42; }
ben tu vas d'abord construire le sous-objet a avec son constructeur par
defaut (donc donner a f la valeur 5), PUIS executer le code { a = 42; }
qui va donc te remplacer la veleur de f par 42.
Si on initialise le champs de maniere explicite:
B::B(): a(42) {}
on evite d'initialiser a deux fois.
Cette construction (initialisation explicite) presente PLEIN d'interets
par rapport a faire l'initialisation dans le code.
- d'abord, dans des cas compliques, tu vas initialiser ton objet deux fois.
- ensuite, si tu n'as pas de constructeur par defaut pour A, ben tu ne
pourras PAS construire d'objet A autrement qu'en initialisant directement
le champs (c'est pour ca qu'on recommande chaudement d'avoir des constructeurs
par defaut quand c'est possible, entre autres... enfin surtout parce que
la STL ne sait pas faire autrement).
- la double initialisation va aussi ne pas fonctionner dans le cas d'un
champs const, qui DOIT etre cree directement a la bonne valeur. Ni du coup
si tu as un champs qui est une reference, puisque c'est une sorte de
"pointeur constant deguise" si j'ose dire (inchangeable apres sa creation)
Note que c'est extremement simple d'instrumenter tout ca, par exemple avec
A(int i = 5): f(i)
{ std::cout << "creation d'un A qui vaut " << i << "n";}
A& operator=(int i)
{ std::cout << "A valait "<< f << " et vaut " << i << "n";
f = i; return *this;}
Pour la destruction, ca va necessairement suivre l'ordre inverse:
- on commence par "demonter" l'objet englobant B
- puis les sous-objets dans l'ordre inverse de declaration.
L'ordre des sous-objets se voit tres bien.
Si tu definis:
struct P {
int x;
int y;
P(): y(0), x(0) {}
};
tous les compilos vont raler que tu as mis tes champs a l'envers dans ton
constructeur, et qu'ils vont etre construits dans l'ordre canonique de
la classe.
En fait, tu pourrais dire "je peux etre explicite dans un constructeur
donne pour l'ordre", mais en fait, tu n'as aucun moyen de specifier dans
quel ordre les sous-objets seront detruits, et donc le langage regle le
probleme en imposant l'ordre de construction <-> destruction comme etant
inverses l'un de l'autre...
Avatar
pasletot
Le mercredi 05 Juillet 2017 à 09:53 par Pasletot :
Bonjour,
Débutant C++ circonspect.
Hypothèse : obtenir en retour de « a.foo() » et de «
b.bar(« ) une seule valeur de « m_x ».
Valeurs obtenues en retour de a.foo() et de b.bar() : m_x = 55 ; et
m_x = 88 .
Le programme sous forme condensée.
int main()
{
A a(55); // m_x
B b(88); // m_a
a.foo();
b.bar();
return 0;
}
// Classe A // attribut « int m_x ;
»
A::A(int x): m_x(x) {} // x = 55
int A::foo() const
{ return m_x; }
// Classe B // attribut « A m_a
; »
B::B(int z) : m_a(z) {} // z = 88
int B::bar() const
{ return m_a.foo();}
Résultats partiels obtenus sur console .
A a ; // Création objet A
Constructeur de m_x ; // oui
Valeur de « m_x » = 55 ;
Adresse de m_x et de « a » 0x28FF0C
B b ; // Création objet B
Constructeur de m_x ; // ??
Valeur de « m_x » = 88
Adresse de m_x et de « b » 0x28FF08
Constructeur de m_a
Adresse de m_a dans B ::B() 0x28FF08
La réinitialisation de « m_x » sous forme de copie de
« m_x » lors de l’appel de « B b » explique (pour
moi) les résultats obtenus, y compris lorsque « m_x » est
initialisé par int « m_x(int) » (retours de la même
valeur).
Par contre l’appel du constructeur de « A » par « B b()
» et la réinitialisation d’une « m_x » à
la valeur de « m_a » me désorientent.
Pouvez-vous m’apporter des éléments de réponse sur
la raison pour laquelle « B b » appelle le constructeur de «
A » et réinitialise « m_x » lorsque c’est
possible ( « x » non int) ?
Merci
Bonjour Olivier, Bonjour espie
Je ne cache pas que la réponse de espie ne m’est pas accessible en première lecture : j’ai besoin de la « dépiauter » et de l’assimiler à ma façon. Dans un premier abord il me semble toutefois qu’elle m’apporte les éclaircissements qui me manquaient . J’ai presque envie de dire « enfin ». D’ores et déjà merci à toi espie.
Tu as Olivier un atout particulier : te mettre au niveau de celui qui demande aide et ne pas tenter de lui en mettre plein la vue. J’ai eu parfois comme réponse sur un site qui prétend enseigner le C++ « si m_x a deux valeurs en retour c’est qu’il prend aussi la valeur de « m_a » » ou encore, malgré les résultats obtenus, que ces derniers sont égaux. Tu as tenté de m’apporter des réponses alors que programmer en C++ ne t’est pas familier. Un merci particulier.
Lorsque j’aurai assimilé la réponse espie ( disponibilité de temps) je reviendrai pour sans doute classer le sujet résolu.
Merci à vous deux
Avatar
pasletot
Le mercredi 05 Juillet 2017 à 09:53 par Pasletot :
Bonjour,
Débutant C++ circonspect.
Hypothèse : obtenir en retour de « a.foo() » et de «
b.bar(« ) une seule valeur de « m_x ».
Valeurs obtenues en retour de a.foo() et de b.bar() : m_x = 55 ; et
m_x = 88 .
Le programme sous forme condensée.
int main()
{
A a(55); // m_x
B b(88); // m_a
a.foo();
b.bar();
return 0;
}
// Classe A // attribut « int m_x ;
»
A::A(int x): m_x(x) {} // x = 55
int A::foo() const
{ return m_x; }
// Classe B // attribut « A m_a
; »
B::B(int z) : m_a(z) {} // z = 88
int B::bar() const
{ return m_a.foo();}
Résultats partiels obtenus sur console .
A a ; // Création objet A
Constructeur de m_x ; // oui
Valeur de « m_x » = 55 ;
Adresse de m_x et de « a » 0x28FF0C
B b ; // Création objet B
Constructeur de m_x ; // ??
Valeur de « m_x » = 88
Adresse de m_x et de « b » 0x28FF08
Constructeur de m_a
Adresse de m_a dans B ::B() 0x28FF08
La réinitialisation de « m_x » sous forme de copie de
« m_x » lors de l’appel de « B b » explique (pour
moi) les résultats obtenus, y compris lorsque « m_x » est
initialisé par int « m_x(int) » (retours de la même
valeur).
Par contre l’appel du constructeur de « A » par « B b()
» et la réinitialisation d’une « m_x » à
la valeur de « m_a » me désorientent.
Pouvez-vous m’apporter des éléments de réponse sur
la raison pour laquelle « B b » appelle le constructeur de «
A » et réinitialise « m_x » lorsque c’est
possible ( « x » non int) ?
Merci
Bonjour espie ;
J’ai viré la ligne avec « this », si je connais de nom je n’ai pas encore bossé dessus. Cette suppression n’ a pas occasionné de « this « de forme (mes excuses : c’est mon tic !)
Si j’ai bien compris j’ai adapté tes explications à mon exemple que je réédite en partiel pour ceux qui prendraient connaissance du sujet :
class A
{
A :: A(int x) : m_x(x) { }
int A::foo(){ return m_x; }
private :
int m_x ;
} ;
et class B
{
B::B() : m_a(99)
int B::bar(){ return m_a.foo();}
private :
A m_a ;
} ;
Main
{
A a(22);
B b;
a.foo();
b.bar();
Résultats console:
Appel A a;
Constructeur de A.
Appel B b;
Constructeur de A // là c’est clair
Constructeur de B
Appel a.foo() ;
-----------------------
Résultats a.foo() = 22 et b.bar() = 99
Par analogie je retiens sans réserve ton explication. Elle me paraît d’autant plus fondée qu’ elle confirme mon sentiment de néophyte sur la prudence avec laquelle il faut aborder la lecture écrite de certaines règles de base n’en déplaise à certains…confirmés plus propres à ânonner qu’a réfléchir.
Pour mon instruction personnelle je considèrerai que quand on initialise « m_x » comme ci-après A ::A(int) : m_x(42) {}, un « int » n’étant pas une variable, la valeur de « m_x » ne peut être modifiée ce qui rend inopérante une réinitialisation, les deux retours sont alors de même valeur à savoir celle celle de « m_x ».
Merci encore, pour moi ce sujet est clos, je retourne à mon étude laborieuse personnelle, sans prétention mais pas sans intérêt..
1 2