Академический Документы
Профессиональный Документы
Культура Документы
abstraction
Introduction :
Nous savons quil est possible de redfinir les mthodes dune classe mre dans une classe fille.
Lors de lappel dune fonction ainsi redfinie, la fonction appele est la dernire fonction
dfinie dans la hirarchie de classe. Pour appeler la fonction de la classe mre alors quelle a
t redfinie, il faut prciser le nom de la classe laquelle elle appartient. Bien que simple, cette
utilisation de la redfinition des mthodes peut poser des problmes.
Supposons quune classe B hrite de sa classe mre A. Si A possde une mthode f() appelant
une autre mthode g() redfinie dans la classe fille B, que se passe-t-il lorsquun objet de
classe B appelle la mthode f ? La mthode f appele tant celle de la classe A, elle appellera
la mthode g de la classe A. Par consquent, la redfinition de g ne sert rien ds quon lappelle
partir dune des fonctions dune des classes mres.
Une premire solution consisterait redfinir la mthode f dans la classe B. Mais ce nest ni
lgant, ni efficace. Il faut en fait forcer le compilateur ne pas faire le lien dans la fonction f
50
de la classe A avec la fonction g de la classe A. Il faut que f appelle soit la fonction g de la
classe A si elle est appele par un objet de la classe A, soit la fonction g de la classe B si elle est
appele pour un objet de la classe B. Le lien avec lune des mthodes g ne doit tre fait quau
moment de lexcution, cest--dire quon doit faire une dition de liens dynamique. Le C++
permet de faire cela. Pour cela, il suffit de dclarer virtuelle la fonction de la classe de base qui
est redfinie dans la classe fille, cest--dire la fonction g.
1. Mthodes virtuelles
Une mthode virtuelle est une mthode prcde par le mot-cl virtual dans la classe de
base.
Les mthodes virtuelles sont des mthodes qui sont appeles selon la vraie classe de lobjet qui
lappelle. Les objets qui contiennent des mthodes virtuelles peuvent tre manipuls en tant
quobjets des classes de base, tout en effectuant les bonnes oprations en fonction de leur type.
Ils apparaissent donc comme tant des objets de la classe de base et des objets de leur classe
complte indiffremment, et on peut les considrer soit comme les uns, soit comme les autres.
Un tel comportement est appel polymorphisme (cest--dire qui peut avoir plusieurs aspects
diffrents).
Une classe possdant des fonctions virtuelles est dite classe polymorphe. La qualification
virtual devant la redfinition d'une fonction virtuelle est facultative : les redfinitions d'une
fonction virtuelle sont virtuelles d'office.
Exemple :
Class Polygone {
...
void toto(double a) {
double b=f(a);
.....}
virtual double f(double a);
...
Class Triangle:public Polygone{
.....
double f(double a);
}
51
Si dans le programme principal, on crit :
Polygone p;
...
p.toto();
La mthode toto appellera la mthode f de Polygone car p est de type Polygone.
Si par contre, on crit :
Triangle t;
...
t.toto();
t tant de type Triangle (qui drive de Polygone), on peut appeler la fonction toto de la classe
Polygone. Mais, ici, la mthode toto appellera la fonction f de la classe Triangle. Cela est d
au fait que la fonction f de la classe Polygone est virtuelle. Si elle ntait pas virtuelle la fonction
toto appellerait systmatiquement la fonction f de la classe Polygone.
Une mthode virtuelle pure est une mthode qui est dclare mais non dfinie dans une classe.
Elle est dfinie dans une des classes drives de cette classe.
Une classe abstraite est une classe comportant au moins une mthode virtuelle pure.
tant donn que les classes abstraites ont des mthodes non dfinies, il est impossible
dinstancier des objets pour ces classes. En revanche, on pourra les rfrencer avec des
pointeurs.
Pour dclarer une mthode virtuelle pure dans une classe, il suffit de faire suivre sa dclaration
de = 0 . La fonction doit galement tre dclare virtuelle :
virtual type nom (paramtres) = 0 ;
= 0 signifie ici simplement quil ny a pas dinstance de cette mthode dans cette classe.
Exemple :
Class Polygone {
...
virtual void CalcAire()=0;
...
}
Utilisation :
Polygone p; // ERREUR : cration dune instance de la classe abstraite Polygone
Polygone *pt; // Oui : cest un pointeur
52
pt = new Polygone; // ERREUR : cration dune instance de la classe abstraite Polygone
Triangle t; // Oui : la classe Triangle nest pas abstraite
pt = new Triangle; // Oui
Le mcanisme des mthodes virtuelles pures et des classes abstraites permet de crer des classes
de base contenant toutes les caractristiques dun ensemble de classes drives, pour pouvoir
les manipuler avec un unique type de pointeurs. En effet, les pointeurs des classes drives sont
compatibles avec les pointeurs des classes de base, on pourra donc rfrencer les classes
drives avec des pointeurs sur les classes de base, donc avec un unique type sous-jacent : celui
de la classe de base. Cependant, les mthodes des classes drives doivent exister dans la classe
de base pour pouvoir tre accessibles travers le pointeur sur la classe de base. Cest ici que
les mthodes virtuelles pures apparaissent. Elles forment un moule pour les mthodes des
classes drives, qui les dfinissent. Bien entendu, il faut que ces mthodes soient dclares
virtuelles, puisque laccs se fait avec un pointeur de classe de base et quil faut que ce soit la
mthode de la classe relle de lobjet (cest--dire la classe drive) qui soit appele.
Le C++ est un langage fortement typ. Malgr cela, il se peut que le type exact dun objet soit
inconnu cause de lhritage. Par exemple, si un objet est considr comme un objet dune
classe de base de sa vritable classe, on ne peut pas dterminer a priori quelle est sa vritable
nature. Cependant, les objets polymorphiques (qui, rappelons-le, sont des objets disposant de
mthodes virtuelles) conservent des informations sur leur type dynamique, savoir leur
vritable nature. En effet, lors de lappel des mthodes virtuelles, la mthode appele est la
mthode de la vritable classe de lobjet.
Utilisation :
Pixel px(1, 2, "rouge");
Point pt = px; // un pixel a t mis l o un point tait
// attendu : il y a conversion implicite
De manire interne, la conversion d'un D vers un B est traite comme l'appel d'une fonction
membre de D qui serait publique, protge ou prive selon le mode (ici : public) dont D drive
de B.
Ainsi, la conversion d'une classe drive vers une classe de base prive ou protge existe mais
n'est pas utilisable ailleurs que depuis l'intrieur de la classe drive. Bien entendu, le cas le
plus intressant est celui de la drivation publique.
Selon qu'elle s'applique des objets ou des pointeurs (ou des rfrences) sur des objets, la
conversion d'un objet de la classe drive vers la classe de base recouvre deux ralits trs
diffrentes :
1. Convertir un objet D vers le type B c'est lui enlever tous les membres qui ne font pas
partie de B (les membres spcifiques et ceux hrits d'autres classes). Dans cette
conversion il y a perte effective d'information :
54
ses membres :
55
void fon3(Point &rf) {
Il n'est rien arriv l'objet qui a servi initialiser rf lors de l'appel de cette fonction.
Si on peut garantir que cet objet est un Pixel, l'expression suivante (place sous la
responsabilit du programmeur) a un sens :
((Pixel &) rf).couleur ...
}
56
Le cot du polymorphisme, en espace mmoire, est d'un pointeur par objet, quel que soit le
nombre de fonctions virtuelles de la classe. Chaque objet d'une classe polymorphe comporte un
membre de plus que ceux que le programmeur voit : un pointeur vers une table qui donne les
adresses qu'ont les fonctions virtuelles pour les objets de la classe en question.
D'o lide de profiter de l'existence de ce pointeur pour ajouter au langage, sans cot
supplmentaire, une gestion des types dynamiques qu'il faudrait sinon crire au coup par coup
(probablement a l'aide de fonctions virtuelles). Fondamentalement, ce mcanisme se compose
des deux oprateurs dynamic_cast et typeid.
L'oprateur dynamic_cast :
Le transtypage dynamique permet de convertir une expression en un pointeur ou une rfrence
dune classe, ou un pointeur sur void. Il est ralis laide de loprateur dynamic_cast.
Cet oprateur impose des restrictions lors des transtypages afin de garantir une plus grande
fiabilit :
o il effectue une vrification de la validit du transtypage ;
o il nest pas possible dliminer les qualifications de constance (pour cela, il faut utiliser
loprateur const_cast).
Syntaxe : dynamic_cast<type> (expression)
o type dsigne le type cible du transtypage, et expression lexpression transtyper.
Exemple :
class Animal {
...
virtual void uneFonction() { // il faut au moins une fonction virtuelle
... // (car il faut des classes polymorphes)
}
};
class Mammifere : public Animal {
...
};
class Chien : public Mammifere {
...
};
57
class Caniche : public Chien {
...
};
class Chat : public Mammifere {
...
};
class Reverbere {
...
};
Chien medor;
Animal *ptr = &medor;
...
Mammifere *p0 = ptr;
// ERREUR ( la compilation) : un Animal n'est pas forcment un Mammifre
Mammifere *p1 = dynamic_cast<Mammifere *>(ptr);
// OK : p1 reoit une bonne adresse, car Mdor est un mammifre
Caniche *p2 = dynamic_cast<Caniche *>(ptr);
// OK, mais p2 reoit 0, car Mdor n'est pas un caniche
Chat *p3 = dynamic_cast<Chat *>(ptr);
// OK, mais p3 reoit 0, car Mdor n'est pas un chat non plus
Reverbere *p4 = dynamic_cast<Reverbere *>(ptr);
// OK, mais p4 reoit 0, car Mdor n'est pas un rverbre
L'oprateur const_cast :
La suppression des attributs de constance et de volatilit peut tre ralise grce loprateur
const_cast. Cet oprateur suit exactement la mme syntaxe que les oprateurs
dynamic_cast et static_cast :
const_cast <type> (expression)
Loprateur const_cast peut travailler essentiellement avec des rfrences et des pointeurs.
Il permet de raliser les transtypages dont le type destination est moins contraint que le type
source vis--vis des mots-cls const et volatile.
En revanche, loprateur const_cast ne permet pas deffectuer dautres conversions que les
autres oprateurs de transtypage (ou simplement les transtypages C classiques) peuvent raliser.
Par exemple, il est impossible de lutiliser pour convertir un flottant en entier. Lorsquil travaille
avec des rfrences, loprateur const_cast vrifie que le transtypage est lgal en
convertissant les rfrences en pointeurs et en regardant si le transtypage nimplique que les
attributs const et volatile. const_cast ne permet pas de convertir les pointeurs de
fonctions.
59
Exemple :
class CCTest {
public:
void setNumber( int );
void printNumber() const;
private:
int number;
};
int main() {
CCTest X;
X.setNumber( 8 );
X.printNumber();
}
L'oprateur reinterpret_cast :
60
void(*fp1)() = reinterpret_cast<void(*)()>(f);
// fp1(); undefined behavior
int(*fp2)() = reinterpret_cast<int(*)()>(fp1);
std::cout << std::dec << fp2() << '\n'; // safe
L'oprateur typeid :
Le C++ fournit loprateur typeid afin de rcuprer les informations de type des expressions.
Sa syntaxe est la suivante :
Typeid (expression)
o expression est lexpression dont il faut dterminer le type.
Le rsultat de loprateur typeid est une rfrence sur un objet constant de classe
type_info.
Les informations de type rcupres sont les informations de type statique pour les types non
polymorphiques. Cela signifie que lobjet renvoy par typeid caractrisera le type de
lexpression fournie en paramtre, que cette expression soit un sous-objet dun objet plus driv
ou non.
Exemple :
Animal *ptr = new Caniche;
cout << typeid(ptr).name() << '\n';
cout << typeid(*ptr).name() << '\n';
cout << "L'animal point par ptr "
<< (typeid(*ptr) == typeid(Chien) ? "est" : "n'est pas")
<< " un chien\n";
cout << "L'animal point par ptr est un "
<< typeid(*ptr).name() << "\n";
Affichage obtenu :
Animal *
Chien
61
L'animal point par ptr n'est pas un chien
L'animal point par ptr est un Caniche
62