Témoignage
 

Rubrique « Info »

 



Écrivez à AILES !



Retour vers programmation



Retour vers les questions C++



Retour vers les questions sur
l'héritage privé
 


Questionnaire C++
Héritage privé

[AnnotatedC++], pp. 242 et suiv.


Finalement, l'héritage privé peut s'avérer très efficace lorsqu'il s'agit de "masquer" les caractéristiques des classes de base. Cela se rencontre surtout lorsque l'on développe une nouvelle couche à partir d'une librairie existante dont on ne souhaite pas trop divulguer les services...
Cela sert également lorsque l'on redéfini un service bien précis d'un objet, et que l'on veut à tout prix éviter qu'un programmeur imprudent utilise, par un upcast "mal placé", le service de la classe de base.
Alors... qu'est-ce que l'héritage privé ? Et c'est quoi "upcast" ?
Voici toutes les réponses!

A quoi sert l'héritage privé ?

Bon, ok. La réponse évidente consiste à dire :
« Une classe qui hérite de façon privée d'une autre rend les méthodes publiques et protégées de sa classe mère (classe de Base "B") en méthodes privées de la classe fille (classe Dérivée "D").
Cela permet de masquer l'interface de la classe mère à tous les utilisateurs de la classe fille
».

ouf.

Mais qu'est qui permet d'être sûr qu'un utilisateur de la classe fille n'utilisera pas les méthodes de la classe mère ?
l'upcast est impossible! En clair, si l'on a :
class B {} ;
class D : private B {} ;
Faire :
D* p_d ;
B* p_b = (B*)p_d ; est impossible
.
(upcast = caster une instance d'une classe fille en celle de la classe mère)
L'upcast peut être :
- explicite comme ci-dessus ou,
- implicite, dans le cas, par exemple, d'une liste de B* ou encore dans le cas où l'on accède, depuis une instance de de D, à une fonction f_b() de B :
p_d->f_b() ; revient en fait à faire l'upcast implicite ((B*)p_d)->f_b() ; ce qui sera interdit par le compilateur dans le cas où D dérive de B de façon "private"!
Si l'on a bien cela à l'esprit, les réponses aux autres questions coulent d'elles-même.



Access specifier for a base class :
class B { /* ... */ } ;
class D1 : B { /* ... */ } ; // héritage private (par défaut) de B
struct D2 : B { /* ... */ } ; // héritage public (par défaut) de B
class D3 : public A, B, C { /* ... */ } ; // héritage public (comme spécifié) de A, mais private (par défaut) de B et de C



Use of a static member of a base class
class B {
public:
   static void f() ;
   void g() ;
};
class D : private B
{

   
B* mem() { return this ; } // ok! l'upcast implicite de D en une classe mère directe privée est autorisée pour les fonctions membre de D (classe fille directe de B) ainsi que pour les fonctions friend de D.
} ;
class DD : public D
{
   void h() ;
};
void DD::h()
{
   
B::f() ; // ok! Cf explication ci-dessous (*)
   
D::f() ; // pas ok : on ne peut pas convertir 'this' en B* (upcast interdit)
   
this->f() ; // pas ok : on ne peut pas convertir 'this' en B* (upcast interdit)
   
this->B::f() ; // pas ok : on ne peut pas convertir 'this' en B* (upcast interdit)
   
f() ; // pas ok : on ne peut pas convertir 'this' en B* (upcast interdit)
   
g() ; // pas ok : on ne peut pas convertir 'this' en B* (upcast interdit)
   
g() ; // pas ok : on ne peut pas convertir 'this' en B* (upcast interdit)
}


(*) Mais pourquoi donc B::f() marche-t-il ??? Cela n'implique-t-il pas de transformer 'this' en B* (upcast?!) ?
En fait non : l'héritage privé n'affecte pas les membres statiques de la classe de base, à condition d'y accéder directement (sans upcast).
B::f() remplit cette condition alors que D::f() revient à écrire ((B*)this)->:f()...
De plus, imaginons que B::f() soit interdit...
Alors, comment expliquer que la fonction globale glob() :
void glob()
{

   B::f() ; // ok!
}
puisse fonctionner sans problème ?
En fait, B::f() ne requiert aucun (up)cast - implicite ou explicite.
En revanche, D::f() en requiert un, ne serait-ce que pour savoir d'où vient cette fonction 'f'...



Access Declaration
class B {
public:
   int a ;
   void f() ;
   void g() ;
private:
   int b ;
   void g( int ) ;
protected:
   int c ;
};
class D : private B
{
public:
   B::a ;
// a redevient un membre public de D
   
B::b ; // *error* : b était privé dans B, il ne peut devenir public dans D
(sinon, ce serait un moyen de violer l'encapsulation des données voulues dans B...)

   void f( int ) ;

 
 B::f ; // *error* l'accès de B::f() ne peut être ajusté car il existe un D::f() de signature différente, mais de même nom!
 
 B::g ; // *error* les fonctions B::g() (au nombre de 2) ne peuvent être rendues *toutes les deux* public car leurs droits d'accès diffèrent. Cela marcherait si elles étaient toutes deux public dans B.
protected:
   
B::c ; // c redevient un membre protected de D
   
B::a ; // *error* a était public sous B, il ne peut devenir protected ou private sous D...
} ;

On comprend bien pourquoi, par héritage, on ne peut augmenter les droits d'accès des variables membre de la classe de base qui se retrouvent dans la classe fille. (Violation de l'encapsulation, comme dans l'exemple de B::b).
Il est moins évident de comprendre pourquoi on ne peut les restreindre :
Si l'on imagine une classe DD héritant publiquement de B, n'est-il pas légitime de se dire « Je ne veux pas que les utilisateur de DD utilisent 'a' (issu de B). Je rend donc a protected ou private dans DD! »
class DD : public B
{
private:
   B::a; // ??? *error*
};
Oui mais : n'importe quel petit malin, bien décidé à utiliser quand même B::a n'a qu'à faire un upcast :
DD* p_dd ;
B* p_monB = (B*)p_dd ;

Ils peuvent ainsi accéder à B::a alors même que celui-ci avait explicitement été déclaré private dans DD...
Or cette même "astuce" ne fonctionnerait pas dans le cas d'un héritage privé (cas de la classe D), puisque l'upcast est impossible...
Alors, pourquoi l'autoriserait-on dans un cas et pas dans l'autre ?...
=> logiquement, toute restriction d'accès ou augmentation d'accès d'une variable ou fonction membre d'une classe de base est interdite dans sa classe fille (que ce soit pour l'héritage public, protected ou private)...
Seule la restitution dans une classe fille des droits d'accès issus de la classe mère est autorisée.
Cette "règle" explique d'ailleurs toutes les autres réponses à cette question.


               
 
Avertissement !
 
Décollage !  |  Présentation du site web "AILES"  | 
Infos générales  |  articles "Informatique"