Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

Problème d'héritage

15 réponses
Avatar
Philip K. Dick
Hello

J'ai une classe Point :

class Point {
public:
Point(int x=0, int y=0);
Point(const Point & p);
Point & operator = (const Point & p);
~Point();
Point symetric ();
protected:
int x;
int y;
};
dont la méthode symetric() retourne un Point de coordonnées (-x,-y)

La classe Pixel en hérite (avec un attribut couleur en plus)
et définit aussi symetric() qui retourne un Pixel :

class Pixel : public Point {
public:
Pixel(int x=0, int y=0, short color=0);
Pixel(const Pixel & p);
Pixel & operator = (const Pixel & p);
~Pixel();
Pixel symetric ();
protected:
short color;
};

Je voudrais écrire Pixel::symetric en réutilisant Point::symetric.
J'avais écrit :

Pixel::Pixel Pixel::symetric () {
Point p = this->Point::symetric();
Pixel q (p.x, p.y, this->color);
return q;
};

mais :
1) ça n'a pas l'air très économique (j'aurais aimé utiliser Point::symetric() sur un Pixel)
2) Le compilateur me jette, il ne veut pas de p.x ou p.y,
pourtant les attributs x et y sont protected ???
pixel.cc: In method `class Heritage::Pixel Heritage::Pixel::symetric()':
point.h:17: `int Heritage::Point::x' is protected
pixel.cc:27: within this context

Une idée ?

PKD

10 réponses

1 2
Avatar
alex
salut,

Pixel::Pixel Pixel::symetric () {
Point p = this->Point::symetric();
Pixel q (p.x, p.y, this->color);
return q;
};


j'aurais quant à moi écrit un constructeur de pixel à partir d'un point et
d'un color, ce qui donnerait :

Pixel Pixel::symetric()
{
return Pixel(Point::symetric(), color);
}

en passant, j'aurais défini cette fonction const.


1) ça n'a pas l'air très économique (j'aurais aimé utiliser
Point::symetric() sur un Pixel)


rien ne t'en empeche, mais Point::symetric() renverra toujours un point...
Si tu veux que Point::symetric() puisse servir à un Pixel, tu peux la
définir comme :

void Point::symetric()
{
x = -x;
y = -y;
}

Comme ça, tu n'as pas besoin de la définir dans Pixel (si color ne change
pas) et tu n'as pas besoin d'accéder à x et y dans les classes dérivées. (de
manière générale, les attributs devraient être privés, et non protected. Si
tu as besoin des attributs de l'ancêtre dans le descendant, ça indique
souvent une erreur de conception.)

Avatar
Philip K Dick
salut,

Pixel::Pixel Pixel::symetric () {
Point p = this->Point::symetric();
Pixel q (p.x, p.y, this->color);
return q;
};


j'aurais quant à moi écrit un constructeur de pixel à partir d'un point et
d'un color, ce qui donnerait :

Pixel Pixel::symetric()
{
return Pixel(Point::symetric(), color);
}

en passant, j'aurais défini cette fonction const.


Merci Alex pour ta réponse. Effectivement, c'est mieux
que ce que j'avais fait :-)

1) ça n'a pas l'air très économique (j'aurais aimé utiliser
Point::symetric() sur un Pixel)


rien ne t'en empeche, mais Point::symetric() renverra toujours un point...
Si tu veux que Point::symetric() puisse servir à un Pixel, tu peux la
définir comme :

void Point::symetric()
{
x = -x;
y = -y;
}

Comme ça, tu n'as pas besoin de la définir dans Pixel (si color ne change
pas) et tu n'as pas besoin d'accéder à x et y dans les classes dérivées.


Par contre, ça ne fais pas ce que je voulais car ça change les attributs
de l'objet.

(de manière générale, les attributs devraient être privés, et non
protected. Si
tu as besoin des attributs de l'ancêtre dans le descendant, ça indique
souvent une erreur de conception.)


Peux-tu m'expliquer en quoi cela indique une erreur de conception ?
Perso, j'ai le Delannoy comme bouquin de C++ et nulle part je n'ai
lu ça (alors que le Stroustrup le dit, il est vrai, mais j'aimerais
comprendre).


Avatar
James Kanze
On Mar 28, 6:35 pm, "Philip K. Dick" wrote:

J'ai une classe Point :

class Point {
public:
Point(int x=0, int y=0);
Point(const Point & p);
Point & operator = (const Point & p);
~Point();
Point symetric ();
protected:
int x;
int y;
};
dont la méthode symetric() retourne un Point de coordonnées (-x,-y)

La classe Pixel en hérite (avec un attribut couleur en plus)


Est-ce réelement ce que tu veux, de pouvoir utiliser des Pixel
partout où tu utilises des Point ? Je ne connais pas ton
application, mais très souvent, c'est plutôt l'encapsulation
qu'on veut : que Pixel contient un Point. Sinon...

et définit aussi symetric() qui retourne un Pixel :

class Pixel : public Point {
public:
Pixel(int x=0, int y=0, short color=0);
Pixel(const Pixel & p);
Pixel & operator = (const Pixel & p);
~Pixel();
Pixel symetric ();
protected:
short color;
};

Je voudrais écrire Pixel::symetric en réutilisant Point::symetric.

Pixel::Pixel Pixel::symetric () {
Point p = this->Point::symetric();
Pixel q (p.x, p.y, this->color);
return q;
};

mais :
1) ça n'a pas l'air très économique (j'aurais aimé utiliser Point ::symetric() sur un Pixel)


Dans la mesure où la fonction renvoie un objet (ce qui me semble
une bonne idée en générale -- je n'aime pas les objets qui
change de valeur en dessous de moi), tu ne peux pas.
Point::symetric() renverra toujous un Point, jamais un Pixel.

Je crois que la suggestion d'Alex est la bonne : que le
constructeur de Pixel prend un Point, et non x et y.
Pixel::symetric() devient alors simplement :

return Pixel( Point::symetric(), color ) ;

2) Le compilateur me jette, il ne veut pas de p.x ou p.y,
pourtant les attributs x et y sont protected ???
pixel.cc: In method `class Heritage::Pixel Heritage::Pixel::symetric ()':
point.h:17: `int Heritage::Point::x' is protected
pixel.cc:27: within this context


C'est la règle. Protected permet l'accès dans une classe dérivée
aux éléments de la classe de base seulement si l'objet est de la
même classe (ou d'une classe dérivée) que celle qui fait
l'accès.

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Avatar
alex
bonjour,
(de manière générale, les attributs devraient être privés, et non
protected. Si
tu as besoin des attributs de l'ancêtre dans le descendant, ça indique
souvent une erreur de conception.)


Peux-tu m'expliquer en quoi cela indique une erreur de conception ?
Perso, j'ai le Delannoy comme bouquin de C++ et nulle part je n'ai
lu ça (alors que le Stroustrup le dit, il est vrai, mais j'aimerais
comprendre).


protected ne protège pas vraiment : il suffit d'hériter en mode public et
hop, tu as accès aux attributs. Pouvoir s'en servir correctement suppose une
connaissance précise de l'implémentation de la classe, connaissance que l'on
a rarement. En général, les attributs sont donc privés. Les méthodes
virtuelles souvent aussi, même si ce n'est pas une règle d'or gravée dans le
marbre ;-) . Les membres protected sont souvent des fonctions à mi-chemin
entre le "bas" et le "haut" niveau , genre utilitaire pour l'objet, qui doit
conserver son sens même dans une classe dérivée puisqu'il sera accessible.


Avatar
Loïc Joly
Les membres protected sont souvent des fonctions à mi-chemin
entre le "bas" et le "haut" niveau , genre utilitaire pour l'objet, qui doit
conserver son sens même dans une classe dérivée puisqu'il sera accessible.



Un cas classique, c'est celui où l'implémentation de la classe la plus
dérivée a besoin d'appeler l'implémentation de la classe juste au
dessus, qui elle même... jusqu'à la classe de base. C'est très courant
par exemple pour sérialiser ou afficher une classe.

--
Loïc

Avatar
Philip K. Dick
Loïc Joly wrote:
Les membres protected sont souvent des fonctions à mi-chemin entre le
"bas" et le "haut" niveau , genre utilitaire pour l'objet, qui doit
conserver son sens même dans une classe dérivée puisqu'il sera
accessible.



Un cas classique, c'est celui où l'implémentation de la classe la plus
dérivée a besoin d'appeler l'implémentation de la classe juste au
dessus, qui elle même... jusqu'à la classe de base. C'est très courant
par exemple pour sérialiser ou afficher une classe.


OK, mais dans l'exemple :

class Point {
public:
(...)
virtual afficher ();
protected:
int x;
int y;
};

class Pixel : public Point {
public:
(...)
afficher ();
protected:
short color;
};

Je souhaite que Point affiche par exemple : "(x,y)"
et que Pixel affiche "(x,y,color)".
Si x et y sont privés, je suis obligé de définir
des accesseurs (get_x(), get_y()) appelés dans la
classe dérivée.

C'est un peu lourd, non ?

PKD


Avatar
Philip K. Dick
Loïc Joly wrote:
Les membres protected sont souvent des fonctions à mi-chemin entre le
"bas" et le "haut" niveau , genre utilitaire pour l'objet, qui doit
conserver son sens même dans une classe dérivée puisqu'il sera
accessible.



Un cas classique, c'est celui où l'implémentation de la classe la plus
dérivée a besoin d'appeler l'implémentation de la classe juste au
dessus, qui elle même... jusqu'à la classe de base. C'est très courant
par exemple pour sérialiser ou afficher une classe.


OK, mais dans l'exemple :

class Point {
public:
(...)
virtual afficher ();
protected:
int x;
int y;
};

class Pixel : public Point {
public:
(...)
afficher ();
protected:
short color;
};

Je souhaite que Point affiche par exemple : "(x,y)"
et que Pixel affiche "(x,y,color)".
Si x et y sont privés, je suis obligé de définir
des accesseurs (get_x(), get_y()) appelés dans la
classe dérivée.

C'est un peu lourd, non ?

PKD


Avatar
alexandre
bonjour,

OK, mais dans l'exemple :

class Point {
public:
(...)
virtual afficher ();
protected:
int x;
int y;
};

class Pixel : public Point {
public:
(...)
afficher ();
protected:
short color;
};

Je souhaite que Point affiche par exemple : "(x,y)"
et que Pixel affiche "(x,y,color)".
Si x et y sont privés, je suis obligé de définir
des accesseurs (get_x(), get_y()) appelés dans la
classe dérivée.

C'est un peu lourd, non ?

non, tu peux mieux faire :


class Point {
public:
(...)
void afficher () const
{
std::cout<<'(';
afficheravant(); // c'est une "porte d'entrée"
std::cout<<x<<','<<y;
afficherapres(); // la aussi
std::cout<<')';
}
private:
virtual void afficheravant() const{}
virtual void afficherapres() const {}
int x;
int y;
};

class Pixel : public Point {
public:
private:
short color;
void afficherapres() const
{
std::cout<<','<<color;
}
};

Avatar
Sylvain
James Kanze wrote on 29/03/2007 09:12:

2) Le compilateur me jette, il ne veut pas de p.x ou p.y,
pourtant les attributs x et y sont protected ???


C'est la règle. Protected permet l'accès dans une classe dérivée
aux éléments de la classe de base seulement /si l'objet est de
la même classe (ou d'une classe dérivée) que celle qui fait
l'accès./


la classe (d'objet) qui manipule les champs hérités est nécessairement
de la "même classe ou dérivée" que celle (le parent) ayant défini ces
champs.

je dirais plutôt si les champs protégés impliqués appartiennent à
l'instance courante, en propre par sa classe ou hérité d'une de ses
super-classes.

void Pixel::foo(){
x = 2 * x;
y = 3 * y;
};

est valide

void Pixel::bar(){
Point pt(*this);
x = 2 * pt.x;
y = 3 * pt.y;
}

est invalide 'pt' n'est pas 'this'; dans le scope de 'bar' c'est une
variable bien distincte avec ses règles propres, le fait que 'this' et
'pt' partagent un parent commun ne doit pas de droit particulier.

Sylvain.


Avatar
James Kanze
On Mar 31, 11:30 pm, Sylvain wrote:
James Kanze wrote on 29/03/2007 09:12:

2) Le compilateur me jette, il ne veut pas de p.x ou p.y,
pourtant les attributs x et y sont protected ???


C'est la règle. Protected permet l'accès dans une classe dérivée
aux éléments de la classe de base seulement /si l'objet est de
la même classe (ou d'une classe dérivée) que celle qui fait
l'accès./


la classe (d'objet) qui manipule les champs hérités est nécessairem ent
de la "même classe ou dérivée" que celle (le parent) ayant défini ces
champs.


Je me suis probablement mal exprimé. (À vrai dire, je ne trouve
pas une façon claire à expliquer la chose.) Si tu as une classe
Base, qui définit des éléments protégés, une classe qui en
dérive, Derived, ne peut accéder aux éléments protégés que dans
les Base des Derived, et non dans les Base tout court, ni des
Base d'autres classes dérivées.

je dirais plutôt si les champs protégés impliqués appartiennent à
l'instance courante, en propre par sa classe ou hérité d'une de ses
super-classes.

void Pixel::foo(){
x = 2 * x;
y = 3 * y;

};

est valide

void Pixel::bar(){
Point pt(*this);
x = 2 * pt.x;
y = 3 * pt.y;
}

est invalide 'pt' n'est pas 'this'; dans le scope de 'bar' c'est une
variable bien distincte avec ses règles propres, le fait que 'this' et
'pt' partagent un parent commun ne doit pas de droit particulier.


Non. La protection en C++ ne se base jamais sur l'instance, mais
toujours sur le type. Considérons :

class Base
{
protected:
int x ;
} ;

class Derived : public Base
{
public:
void f() ;
} ;

class OtherDerived : public Base
{
} ;

void
Derived::f()
{
Base b ;
Derived d1 ;
OtherDerived d2 ;
int i ;

i = this->x ; // (ou simlement x) légal
i = b.x ; // illégal
i = d1.x ; // légal
i = d2.x ; // illégal
}

La règle, c'est que la classe Derived peut accéder aux membres
de la classe Base si et seulement si le type static de
l'expression d'accès a un type Derived (ou un type dérivé de
Derived).

Il y avait des bonnes raisons pour ceci. Mais je ne me les
rappelle plus, et vue la confusion qu'il cause, je me démande si
c'était vraiment une bonne idée, quelque soit la validité des
raisons.

--
James Kanze (Gabi Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34



1 2