1 Héritage
2 Fonctions virtuelles
3 Héritage multiple
4 Type de données abstraites
Declaration de la dérivation
La notion d'héritage est assez spontanée : certains concepts sont voisins les uns des autres et généralisables. Par exemple, des Action ou
des Obligations sont des produits financiers, à ce titre, elles ont des caractéristiques communes :
#include <iostream> #include <vector> using namespace::std; class ActifFinancier { public : ActifFinancier(); ~ActifFinancier(); vectorlisteCours; double calculRentabilite(int date1,int date2); void getCours(String str); } class Action : public ActifFinancier { public : Action(); ~Action(); } class Obligation : public ActifFinancier { public : Obligation(int tauxNominal, double valeurNominale,int nombreObligations, int duree); ~Obligation(); /*le taux d'interet nominal*/ double tauxNominal; double valeurNominale; int nombreObligations; int duree; double calculInteretEcheance(); }
Action societeGenerale; societeGenerale.getCours("c:\\Cours\\SG.txt");
Lors de la construction d'un objet de type Action, il y a d'abord construction d'un objet de type ActifFinancier puis construction d'un objet de type Action. En l'absence de paramètres, c'est le constructeur par défaut qui est appelé. A l'inverse, lors de la destruction d'un objet Action, c'est d'abord le desctructeur de Action qui est appelé puis celui de ActifFinancier. Il est bien sur possible de surcharger les constructeurs que ce soit une classe dérivée ou une classe qui est dérivée. Dans ce cas, on indique le constructeur de la classe mère utilisé par chacun des constructeurs des classes dérivées :
#include <iostream> #include <vector> #include <string> using namespace::std; class ActifFinancier { public : ActifFinancier(); ActifFinancier(string cheminFichier); ~ActifFinancier(); vectorlisteCours; double calculRentabilite(int date1,int date2); void getCours(string str); }; class Action : public ActifFinancier { public : Action(); Action(string chemin); ~Action(); }; class Obligation : public ActifFinancier { public : Obligation(double tauxNominal, double valeurNominale,int nombreObligations, int duree); Obligation(string chemin,double tauxNominal, double valeurNominale,int nombreObligations, int duree); ~Obligation(); /*le taux d'interet nominal*/ double tauxNominal; double valeurNominale; int nombreObligations; int duree; double calculInteretEcheance(); }; /*Les fonctions de ActifFinancier*/ ActifFinancier::ActifFinancier() { std::cout << "Le constructeur ActifFinancier " << this << "\n"; } ActifFinancier::ActifFinancier(string chemin) { std::cout << "Le constructeur ActifFinancier(string) " << this << "\n"; getCours(chemin); } void ActifFinancier::getCours(string chemin) { //on devrait definir ici une fonction de renseignement du cours depuis le chemin } ActifFinancier::~ActifFinancier() { std::cout << "Destructeur de ActifFinancier " << this << "\n"; } /*Les fonctions de Action*/ Action::Action() : ActifFinancier() { std::cout << "Le constructeur Action " << this << "\n"; } Action::Action(string chemin) : ActifFinancier(chemin) { std::cout << "Le constructeur Action(string) " << this << "\n"; } Action::~Action() { std::cout << "Destructeur de Action " << this << "\n"; } /*les fonctions de Obligations*/ Obligation::Obligation(double tauxNominal, double valeurNominale,int nombreObligations, int duree) : ActifFinancier() { std::cout << "Le constructeur Obligation(double tauxNominal, double valeurNominale,int nombreObligations, int duree) " << this <<"\n"; this->tauxNominal=tauxNominal; this->valeurNominale=valeurNominale; this->nombreObligations=nombreObligations; this->duree=duree; } Obligation::Obligation(string chemin,double tauxNominal, double valeurNominale,int nombreObligations, int duree) : ActifFinancier(chemin) { std::cout << "Le constructeur Obligation(string chemin,double tauxNominal, double valeurNominale,int nombreObligations, int duree)" << this <<"\n"; this->tauxNominal=tauxNominal; this->valeurNominale=valeurNominale; this->nombreObligations=nombreObligations; this->duree=duree; } Obligation::~Obligation() { std::cout << "Destructeur de Obligation " << this << "\n"; } int main() { Action a1; Action a2("c:\\j2sdk\\t.txt"); Obligation o1(0.05,100,10,5); Obligation o2("c:\\j2sdk\\t.txt",0.05,100,10,5); return 0; }
Le constructeur ActifFinancier 0x22ff50 Le constructeur Action 0x22ff50 Le constructeur ActifFinancier(string) 0x22ff40 Le constructeur Action(string) 0x22ff40 Le constructeur ActifFinancier 0x22fef0 Le constructeur Obligation(double tauxNominal, double valeurNominale,int nombreObligations, int duree) 0x22fef0 Le constructeur ActifFinancier(string) 0x22fec0 Le constructeur Obligation(string chemin,double tauxNominal, double valeurNominale,int nombreObligations, int duree)0x22fec0 Destructeur de Obligation 0x22fec0 Destructeur de ActifFinancier 0x22fec0 Destructeur de Obligation 0x22fef0 Destructeur de ActifFinancier 0x22fef0 Destructeur de Action 0x22ff40 Destructeur de ActifFinancier 0x22ff40 Destructeur de Action 0x22ff50 Destructeur de ActifFinancier 0x22ff50
Dans le cas où on déclare deux fonctionx dans la classe Action et dans la classe ActifFinancier :
class ActifFinancier { public : ActifFinancier(); ActifFinancier(string cheminFichier); ~ActifFinancier(); vectorDans ce cas, l'appel suivant :listeCours; double calculRentabilite(int date1,int date2); void getCours(string str); }; class Action : public ActifFinancier { public : Action(); Action(string chemin); ~Action(); void getCours(string str); };
Action a; a.getCours("c:\\j2sd\\cours\\cours.txt"); a.ActionFinancier::getCours("c:\\j2sd\\cours\\cours.txt");
Action a; a.getCours("c:\\j2sd\\cours\\cours.txt"); a.getCours();La première fonction exécutée est celle définie dans Action, la seconde est celle définie dans ActifFinancier.
Les fonctions virtuelles permettent de manipuler des listes d'ActifFinancier en manipulant des pointeurs :
#include <iostream> #include <vector> #include <string> using namespace::std; class ActifFinancier{ public : double calculRentabilite(int d1,int d2); }; class Action : public ActifFinancier{ public : double calculRentabilite(int d1,int d2); }; class Obligation : public ActifFinancier{ public : double calculRentabilite(int d1,int d2); }; double ActifFinancier::calculRentabilite(int d1,int d2) { std::cout << "calculRentabilie de ActifFinancier\n"; return 0.5; } double Action::calculRentabilite(int d1,int d2) { std::cout << "calculRentabilie de Action\n"; return 1.5; } double Obligation::calculRentabilite(int d1,int d2) { std::cout << "calculRentabilie de Oligation\n"; return 2.5; } int main() { ActifFinancier *af=new ActifFinancier(); ActifFinancier *a=new Action(); ActifFinancier *ob=new Obligation(); af->calculRentabilite(1,2); a->calculRentabilite(1,2); ob->calculRentabilite(1,2); return 0; }
calculRentabilie de ActifFinancier calculRentabilie de ActifFinancier calculRentabilie de ActifFinancier
#include <iostream> #include <vector> #include <string> using namespace::std; class ActifFinancier{ public : virtual double calculRentabilite(int d1,int d2); }; class Action : public ActifFinancier{ public : double calculRentabilite(int d1,int d2); }; class Obligation : public ActifFinancier{ public : double calculRentabilite(int d1,int d2); }; double ActifFinancier::calculRentabilite(int d1,int d2) { std::cout << "calculRentabilie de ActifFinancier\n"; return 0.5; } double Action::calculRentabilite(int d1,int d2) { std::cout << "calculRentabilie de Action\n"; return 1.5; } double Obligation::calculRentabilite(int d1,int d2) { std::cout << "calculRentabilie de Obligation\n"; return 2.5; } int main() { ActifFinancier *af=new ActifFinancier(); ActifFinancier *a=new Action(); ActifFinancier *ob=new Obligation(); af->calculRentabilite(1,2); a->calculRentabilite(1,2); ob->calculRentabilite(1,2); return 0; }En ayant déclarer la méthode calculRentabilite comme virtuelle :
calculRentabilie de ActifFinancier calculRentabilie de Action calculRentabilie de Obligation
Il est possible de construire une classe héritant de plusieurs classes. Par exemple, une action est une part sociale d'entreprise. On peut donc
envisager que non seulement Action hérite de ActifFinancier, mais également qu'Action hérite d'une classe PartSociale qui définit des fonctions spécifiques.
Dans ce cas, la notation :
#include <string> #include <vector> #include <iostream> using namespace::std; class ActifFinancier { public : vector<double>listeCours; double calculRentabilite(int date1,int date2); ActifFinancier(vector<double> listeCours); }; class PartSociale { public : int getNbPartsSociales(); private : int nbParts; }; class Action : public ActifFinancier, public PartSociale { public : Action(vector<double> liste); }; ActifFinancier::ActifFinancier(vector<double> listeCours) { for(int i=0;i<listeCours.size();i++) { double y=listeCours.at(i); this->listeCours.push_back(y); } } double ActifFinancier::calculRentabilite(int date1,int date2) { double x1=listeCours.at(date1); double x2=listeCours.at(date2); return (x2-x1)/x1; } int PartSociale::getNbPartsSociales() { return nbParts; } Action::Action(vector<double> liste) : ActifFinancier(liste) { } int main() { srand(time(0)); vector<double> liste; for(int i=0;i<100;i++) liste.push_back(rand()/(double)RAND_MAX); Action *a=new Action(liste); std::cout << "Calcul rentabilite : " << a->calculRentabilite(4,6) << "\n"; std::cout << "getNbPartsSociales : " << a->getNbPartsSociales(); }Notez d'abord un élément qui aurait pu être évoqué dans la partie précédente : dans le cas d'une classe Action qui dérive d'une classe ActifFinancier, le constructeur de ActifFinancier est appelé pour construire un objet de type Action. Si le constructeur sans paramètre existe pour ActifFinancier, c'est celui qui est appelé. Dans le cas où on veut appeler un autre constructeur de ActifFinancier pour construire les objets de type Action :
Action::Action(type1 para1) : ActifFinancier(para1) { }Dans le cas où le constructeur par défaut n'existe pas pour ActifFinancier, ce type d'appel est obligatoire.
Action *a; a->f();Provoquera une erreur lors de la compilation. Il faudra préciser les appels :
Action *a; a->PartSociale::f(); a->ActifFinancier::f();
Action::Action(type1 para1, type2 para2, type3 para3) : ActifFinancier(para1), PartSociale(para2,para3) { }
Dans certaines situations, une classe suppose l'existence d'un comportement, mais ce comportement ne peut être implémenté qu'au niveau des classes dérivées.
Par exemple, la classe ActifFinancier appelle à la définition d'un comportement calculRentabilite(int date1,int date2), néanmoins, le calcul de la rentabilité
dépend du type d'actif financier : elle ne se calcule pas de la même façon pour une obligation ou pour une action. La syntaxe est la suivante :
virtual double calculRentabilite(int date1,int date2)=0;
#include <iostream> #include <vector> #include <string> using namespace::std; class ActifFinancier { public : vector<double> listeCours; virtual double calculRentabilite(int date1,int date2)=0; double evolutionCours(int date1,int date2); }; class Action : public ActifFinancier { public : double calculRentabilite(int date1,int date2); }; class Obligation : public ActifFinancier { public : double calculRentabilite(int date1,int date2); }; double Action::calculRentabilite(int date1,int date2) { return 5; } double Obligation::calculRentabilite(int date1,int date2) { return 3; } int main() { return 0; }
vector<ActifFinancier *> actifs; ... for(int i=0;i<actifs.size();i++) { ActifFinancier *aF=actifs.at(i); std::cout << aF->calculRentabilite(5,10) << "\n"; }Que se passe t'il à la compilation si on ne définit pas la fonction de calculRentabilite pour Obligation ? La compilation fonctionne tant que l'on ne cherche pas à instancier d'objet de type Obligation. Si on cherche à instancier un tel objet, cela ne fonctionne pas : une erreur de compilation se déclenche, il est impossible de créer un objet pour cette classe.