Académique Documents
Professionnel Documents
Culture Documents
¤ La nouvelle classe (classe fille, sous-classe) hérite des attributs et des méthodes des
classes à partir desquelles elle a été formée (classes mères, super-classes).
¤ Des méthodes des classes mères peuvent être redéfinies dans la classe fille.
¤ Intérêt :
> Réutilisation Utiliser une classe existante…
+ En lui rajoutant des membres
+ et/ou en modifiant certains de ses comportements
¤ Les classes chat, chien et oiseau sont des classes dérivées (sous-classes).
¤ Une classe dérivée modélise un cas particulier de la classe de base et, est donc
enrichie d'informations supplémentaires.
¤ Souvent, on utilise l’héritage simple : une classe fille a une classe mère.
¤ On parle d’héritage multiple quand une classe fille a plusieurs classes mères.
¤ Intérêts de l’héritage :
> Favoriser la réutilisation.
> Factoriser le code.
> Catégoriser les concepts dans une relation de généralisation Chaque concept est
caractérisé par ses différences par rapport à son (ses) parent(s).
public private
protected inacessibles
private
dans la classe de base dans la classe dérivée
¤ Héritage protégé
public protected
protected inacessibles
private
COURS DE PROGRAMMATION EN C++ 2019-2020 ENSA
ENSAKENITRA
KENITRA––UNIVERSITÉ
UNIVERSITÉIBN
IBNTOUFAIL
TOUFAIL 9
HÉRITAGE : REDÉFINITION DES FONCTIONS MEMBRES
¤ La fonction affiche de l’exemple précèdent est membre de la classe de base véhicule.
¤ Elle n'affiche que les membres privés de cette classe.
¤ On ne peut pas donc afficher le nombre de moteurs dans la sous-classe avion par exemple.
¤ Pour faire cela, nous allons définir dans la classe dérivée une fonction portant le même nom
et, qui aura pour rôle d'afficher les données privées de la classe dérivée.
¤ On parle alors de redéfinition d'une fonction de la classe de base.
¤ Quand une méthode est redéfinie dans la classe fille, elle doit être déclarée avec la même
signature que dans la classe mère.
¤ Le code d’une méthode redéfinie fait souvent appel à la méthode de la classe mère (super
méthode) NomDeLaClasseMere::NomDeLaMethode(arguments)
¤ Les constructeurs des classes mères ne peuvent être utilisés pour construire des
instances de la classe fille
> Il faut définir de nouveaux constructeurs.
¤ Les constructeurs de la classe fille font appel aux constructeurs des classes mères au
début de la liste d’initialisations.
> Pour créer un avion il faut d’abord créer un vehicule
¤ Si la classe de base a un constructeur autre que celui par défaut, la classe dérivée doit
avoir un constructeur.
¤ Le destructeur des classes mères est toujours appelé implicitement après l’exécution
du destructeur de la classe fille.
¤ Les droits d'accès protègent les données et les méthodes et réalisent aussi
l'encapsulation.
¤ Les droits d'accès sont accordés aux fonctions membres ou aux fonctions globales.
¤ L'unité de protection est la classe: tous les objets de la classe bénéficient de la même
protection.
¤ Il y a 3 catégories de protection:
> un membre public est accessible à toute fonction,
> un membre private n'est accessible qu'aux fonctions membres de la classe ou aux
fonctions amies,
> un membre protected n'est accessible qu'aux fonctions membres de la classe de
base ou des classes dérivées ou aux fonctions amies.
¤ Un objet d'une classe dérivée peut toujours être utilisé au lieu d'un objet de sa classe
de base. (N’est applicable que dans le cas de la dérivation public).`
¤ Par exemple, un avion est un véhicule. Mais l'inverse n'est pas vrai, un véhicule n'est
pas nécessairement un avion.
¤ Conversion implicite de tout avion EST-UN véhicule. Le compilateur fait une copie en
ignorant les membres excédentaires (nbre_moteurs).
¤ Un véhicule n'est pas forcément un avion. On ne peut pas deviner quelles seront les
valeurs manquantes (dans cet exemple: nbre_moteurs). Un véhicule n'a pas toutes les
données d'un avion.
¤ Traiter un type dérivé comme s’il était son type de base est appelé transtypage
ascendant ou surtypage (upcasting).
p A a; B b; C c;
p a=b; // Ok
p a=c; // Ok
p b=c; // Ok
p b=a; // NOK
p c=a;//NOK 84
p L’héritage permet:
n Concevoir une architecture solide
n Développement en mode bloc
n La réutilisation du code déjà écrit
n L’ajout de nouvelles fonctionnalités
n La modification d’un comportement existant
(redéfinition)
p ]
class A { class B {
public: public:
A () { B (int a, int b, int c){
cout<<« Je suis A()»endl; cout<<« Je suis B()»endl;
} }
A (int i) { B (int a){
cout<<« Je suis A(int)»endl; cout<<« Je suis B(int)»endl;
} }
A (int i, int j) { B (int a, int b){
cout<<« Je suis A(int,int)»endl; cout<<« Je suis B(int,int)»endl;
} }
}; };
¤ lors du passage dans la fonction, la vraie nature de la moto s'était perdue et qu'elle
était redevenue un simple véhicule.
¤ résolution statique des liens è C'est le type de la variable qui détermine quelle
fonction membre appeler et non sa vraie nature.
class Vehicule {
public: int main () {
void afficher() { std::cout << "Je suis un Vehicule" << endl; }
Vehicule* v[4];
}; v[0] = new Vehicule();
v[1] = new Auto();
class Moto : public Vehicule { v[2] = new Moto();
public: v[3] = new Camion();
void afficher() { std::cout << "Je suis une Moto" << endl; } Moto* p1 = new Moto();
Auto* p2 = new Auto();
};
Camion* p3 = new Camion();
class Vehicule {
public:
virtual void afficher() { std::cout << "Je suis un Vehicule" << endl; }
};
* 1
orchestre instrument de musique
cuivres bois
¤ Une variables statique est une variable commune de toutes les instances. On dit que
c’est une variable de classes.
¤ Déclaration d’une méthode statique : static void afficher(); // ne pas mettre statique
dans l’implementatin
¤ Parmi les techniques pour améliorer la réutilisabilité des morceaux de code, nous
trouvons la notion de généricité.
¤ Cette notion permet d’écrire du code générique en paramétrant des fonctions et des
classes par un type de données.
¤ Un module générique n’est alors pas directement utilisable : c’est plutôt un modèle,
patron (template) de module qui sera «instancié » par les types de paramètres qu’il
accepte.
¤ Dans C++, grâce à la notion de patron de fonctions, il est possible de définir une famille
de fonctions paramétrées par un ou plusieurs types, et éventuellement des
expressions.
¤ D'une manière comparable, C++ permet de définir des "patrons de classes". Là encore,
il suffira d’écrire une seule fois la définition de la classe pour que le compilateur puisse
automatiquement l'adapter à différents types.
¤ Définition des fonctions min grâce à la surcharge: lors d’un appel à la fonction min, le
type des paramètres est alors considéré et l’implantation correspondante est
finalement appelée.
¤ Une autre solution est de définir une fonction template, c’est-à-dire générique.
¤ Cette définition définit en fait un patron de fonction, qui est instancié par un type de
données (ici le type T) pour produire une fonction par type manipulé.
¤ Définition de la fonction min générique : il n’est donc plus nécessaire de définir une
implementation par type de données.
¤ On définit donc bien plus qu’une fonction, on définit une méthode permettant d’obtenir
une certaine abstraction en s’affranchissant des problèmes de type.
¤ Il est possible de définir des fonctions template acceptant plusieurs types de données
en paramètre. Chaque paramètre désignant une classe est alors précédé du mot-clé
class, comme dans l’exemple : template <class T, class U> ....
¤ Chaque type de données paramètre d’une fonction template doit être utilisé dans la
définition de cette fonction.
¤ Pour que cette fonctionnalité soit disponible, les fonctions génériques doivent être
définies au début du programme ou dans des fichiers d’interface (fichiers .h).
¤ Il est possible, comme pour les fonctions, de définir des classes template, c’est-à-dire
paramétrées par un type de données.
¤ Cette technique évite ainsi de définir plusieurs classes similaires pour décrire un même
concept appliqué à plusieurs types de données différents.
¤ Elle est largement utilisée pour définir tous les types de containers (comme les listes,
les tables, les piles, etc.), mais aussi des algorithmes génériques par exemple.
¤ La syntaxe permettant de définir une classe template est similaire à celle qui permet de
définir des fonctions template.
¤ Comme dans le cas des patrons de fonctions, la mention template <class T> précise
que l'on a affaire à un patron (template) dans lequel apparaît un paramètre de type
nommé T ;
¤ La définition de notre patron de classes n'est pas encore complète puisqu'il y manque
la définition des fonctions membres, à savoir le constructeur point et la fonction
affiche().
¤ Pour ce faire, la démarche va légèrement différer selon que la fonction concernée est
en ligne ou non. Voici par exemple comment pourrait être défini notre constructeur en
ligne:
¤ Voici ce que pourrait être finalement la définition de notre patron de classe point :
¤ Comme pour les patrons de fonctions, l’instanciation de tels patrons est effectuée
automatiquement par le compilateur selon les déclarations rencontrées. Après avoir
créé ce patron, une déclaration telle que:
> point <int> a;
¤ Toutes les fonctions membres d’une classe, amies d’une autre classe.
int *pint;
pint = new int;
If (pint == NULL) {
cout << “Manque de
mémoire pour pint” << endl;
exit(1);
}
¤ Une exception est une rupture de séquence déclenchée par une instruction
“throw” comportant une expression (objet) d’un type (classe) donné.
try
{
//code à tester
}
catch (…) {
//traitement des erreurs
}
¤ Définition de l’exception
class exception_triviale {
…
};
try
{
//code à tester
}
catch (Exception1 &e1) {
//traitement des erreurs
}
catch (Exception2 &e2) {
//traitement des erreurs
}
COURS DE PROGRAMMATION EN C++ 2019-2020 ENSA
ENSAKENITRA
KENITRA––UNIVERSITÉ
UNIVERSITÉIBN
IBNTOUFAIL
TOUFAIL 61
THROW DANS LA DÉCLARATION D’UNE MÉTHODE
¤ Au lieu d’utiliser un bloc try…catch, il est possible de placer un throw sur toute la
méthode.
>Runtime_error
+range_error
+overflow_error
+Underflow_error
¤ Raisons :
#include <iostream.h>
#include <stdlib.h>
#include <stdexcept>
}
return 0;
}
¤ Résultats :
exception: anomalie_1
¤ #include <iostream>
¤ #include <stdlib.h>
¤ #include <stdexcept>
¤
¤
int main(){
try{
Résultat
¤
¤
int i=100;
double epsilon=0.1;
d’exécution:
¤ double somme=0.0;
¤ for(int k=1; k<=100;k=k+10) somme courante10
¤ {
¤ epsilon=epsilon/k; somme courante120
¤ if (epsilon<=0.001)
¤ throw range_error("anomalie_1"); exception: anomalie_1
¤ else
¤ { Press any key to continue
¤ somme=somme+1/epsilon;
¤ cout<<"somme courante"<<somme<<endl;}
¤ }
¤ cout<<"somme = "<<somme<<endl;
¤ }
¤ catch (range_error &re){
¤ cout<<"exception: "<<re.what()<<endl;
¤ }
¤ }
¤ #include "stdafx.h"
¤
¤
#include <iostream>
#include <stdlib.h>
Exception d'allocation de
¤
¤
#include <stdexcept>
#define MAX_NUMBER 1000000000
mémoire par new
¤ using namespace std; Vous êtes trop gourmand!
¤ int _tmain(int argc, _TCHAR* argv[]) bad allocation
¤ {
¤ try Press any key to continue
¤ {
¤ int* ptrTab;
¤ for (int i=0;i<MAX_NUMBER; i++)
¤ ptrTab=new int[MAX_NUMBER];
¤ }
¤ catch (bad_alloc & b) l’exception « bad-alloc »
{
¤
¤ cout<<"Exception d'allocation de mémoire par new"<<endl; est déclenchée par la
¤
¤
cout<<"Vous êtes trop gourmand!"
<<endl<<b.what()<<endl;
fonction « new » de la
¤
¤
}
return 0;
librairie standard.
¤ }
73
ESPACES DE NOMMAGE
#include <iostream>
¤ Syntaxe :
namespace nom
{
déclarations & définitions;
}
¤ Utiliser un namespace :
¤ résolution de portée ::
nom_du_namespace::nom_à_accéder
ou
namespace mon_espace
{
class T
{
T();
~T();
long foo();
};
}
mon_espace::T::~T()
{}
long mon_espace::T::foo()
{
return 0;
}