Héritage Multiple : le problème du diamant en C++

En programmation objet, lorsque l’on a affaire à un héritage multiple tel que les parents de la fille hérite du même parent :

diagrammeHeritageMultiple

Cela peut poser poser problème à la instanciation de la classe fille car son constructeur va faire appel aux constructeurs de Mere 1 , 2 qui eux même vont faire appel à celui de la classe GrandMere. Le compilateur va cracher une erreur du type : error request for member « … » is ambiguous . En effet, ce dernier ne saura pas par qui passer ( Mere 1 ou Mere 2) pour accéder au constructeur de GrandMere.

Afin de résoudre cette ambiguité appelée : » le problème du diamant ». Il est possible de déclarer les classes Mere 1, 2 comme classes virtuelles de GrandMere. Dans ce sens , la classe fille va récupérer les méthodes et l’héritage de ses parents ( et donc de la grand Mère) sans que ces derniers ne fassent appel à leur constructeur.

L’exemple qui suit reprend cette idée, les classes Mere 1 et Mere 2 , on alors deux fonctions propres que la classe fille peut appeler. Les attributs proviennent quand à eux de la classe GM. ( Remarque dans cette exemple , les classes sont déclarées à l’avance dans les .h, les includes des classes sont appelées dans les .cpp pour éviter le problème de dépendance circulaire.

Nous avons donc :

GM.h :

#ifndef GM_H
#define GM_H
#include <string>
using namespace std;
class GM{
public :
int age;
string nom;
};
#endif

GM.cpp (le fichier est vide mais par défaut les constructeurs sont appelés automatiquement) :

#include "GM.h"

Mere1.h :

#ifndef Mere1_H
#define Mere1_H
#include "GM.h"
class Mere1: virtual public GM{
 
public :
void affiche_nom();
};
#endif

Mere1.cpp :

#include "GM.h"
#include "Mere1.h"
#include <iostream>
using namespace std;
 
void Mere1::affiche_nom(){
cout<< "Nom : "<<nom<<endl;
}

Mere2.h :

#ifndef Mere2_H
#define Mere2_H
#include "GM.h"
 
class Mere1;
 
class Mere2: virtual public GM{
public :
void affiche_age(Mere1);
};
 
#endif

Mere2.cpp :

#include "GM.h"
#include "Mere2.h"
#include "Mere1.h"
#include <iostream>
using namespace std;
 
void Mere2::affiche_age(Mere1 M1){
cout<< "Age :"<<age<<endl;
}

Fille.h :

#ifndef Fille_H
#define Fille_H
 #include "Mere2.h"
#include "Mere1.h"
 
class Fille: public Mere1,public Mere2{
 };
#endif

Fille.cpp :

#include "Fille.h"

main.cpp :

#include <iostream>
#include <fstream>
#include <string>
#include "GM.h"
#include "Mere1.h"
#include "Mere2.h"
#include "Fille.h"
using namespace std;
class GM;
class Mere1;
class Mere2;
class Fille;
 
int main(){
 
Fille f;
f.age=12;
f.nom="dupond";
 f.Mere1::affiche_nom();
f.Mere2::affiche_age(f);
return 0;
}

et le Makefile :

CC = g++
GLIBS=  #
CFLAGS =-Wall -c -g
LIBS = #
 
#========Regle pour clean , efface les fichiers indesirables pour un nouveau build====================================================================
 
cleanDebug: clean
cleanRelease: clean
clean:
    rm -rf  *.o *.bin
 
 
all :   herit
 
 
GM.o    :   GM.cpp
    $(CC)   $(CFLAGS)   $(GLIBS)    $(LIBS) GM.cpp
Mere1.o :   Mere1.cpp
    $(CC)   $(CFLAGS)   $(GLIBS)    $(LIBS) Mere1.cpp
Mere2.o :   Mere2.cpp
    $(CC)   $(CFLAGS)   $(GLIBS)    $(LIBS) Mere2.cpp
Fille.o :   Fille.cpp
    $(CC)   $(CFLAGS)   $(GLIBS)    $(LIBS) Fille.cpp
 
main.o  :   main.cpp
    $(CC)   $(CFLAGS)   $(GLIBS)    $(LIBS) main.cpp
 
herit   :   GM.o    Mere1.o Mere2.o Fille.o     main.o 
    $(CC)   $(LFLAGS)    GM.o   Mere1.o Mere2.o Fille.o     main.o      -o  herit   $(GLIBS)