C ++, inheritance virtuel, étrange problème de classe abstraite + clone

Désolé pour la plus grande quantité de code source. Il y a trois classes abstraites P, L, PL. La troisième classe PL est dérivée des classes P et L en utilisant l’inheritance virtuel:

template  //Abstract class class P { public: virtual ~P() = 0; virtual P  *clone() const = 0; }; template  P::~P() {} template  P * P :: clone() const { return new P  ( *this ); } template  //Abstract class class L { public: virtual ~L() = 0; virtual L  *clone() const = 0; }; template  L::~L() {} template  L *L  ::clone() const { return new L  ( *this );} template  class PL: virtual public P , virtual public L  //Abstract class { public: PL() : P (), L() {} virtual ~PL() = 0; virtual PL  *clone() const = 0; }; template  PL::~PL() {} template  PL * PL  :: clone() const { return new PL  ( *this );} 

Chaque classe a sa propre implémentation de la méthode clone.

Les deux classes suivantes PA, PC sont dérivées de la classe P en utilisant l’inheritance virtuel:

 template  class PC : virtual public P  { public: PC() : P  () {} virtual ~PC() {} virtual PC  *clone() const {return new PC  ( *this );} }; template  class PA : virtual public P  { public: PA() : P  () {} virtual ~PA() {} virtual PA  *clone() const {return new PA  ( *this );} }; 

Les deux dernières classes PCL et PAL sont dirived en utilisant l’inheritance virtuel de PC et PL, PA et PL.

 template  class PCL : public PC , public PL  { public: PCL() : P  (), PC  (), PL  () {} virtual ~PCL() {} virtual PCL  *clone() const {return new PCL  ( *this );} }; template  class PAL : public PA , public PL  { public: PAL() : P  (), PA  (), PL  () {} virtual ~PAL() {} virtual PAL  *clone() const {return new PAL  ( *this );} 

};

Il y a le diagramme des dépendances de classe:

 .......... P .... L..... ........../|\..../...... ........./.|.\../....... ......../..|..\/........ .......PC..PA..PL....... .......|...|.../|....... .......|...|../.|....... .......|...PAL..|....... .......|........|....... .......PCL_____/........ 

S’il vous plaît, ne discutez pas de cette proposition :-))). J’ai les 3 questions suivantes:

1) Cette dépendance de classe a-t-elle été réécrite correctement en C ++ (tout d’abord le placement de “virtuel”)?

2) Je ne suis pas sûr de ce qui ne va pas, voir le code, s’il vous plaît:

 int main(int argc, _TCHAR* argv[]) { PCL  * pcl = new PCL (); //Object of abstract class not allowed PAL  * pal = new PAL (); //Object of abstract class not allowed PL  *pl1 = pcl->clone(); //Polymorphism PL  *pl2 = pal->clone(); //Polymorphism return 0; } 

Il n’est pas possible de créer de nouveaux objects de classes PAL / PCL, les deux classes sont marquées comme abstraites. Mais ils ne sont pas abstraits. Où est le problème?

3) Est-il possible d’utiliser un polymorphism avec la méthode clone ()? Voir le code ci-dessus, s’il vous plaît …

Merci de votre aide…


QUESTION MISE À JOUR

J’ai corrigé le code. Mais l’erreur suivante lors de l’utilisation du compilateur VS 2010 apparaît:

 template  //Abstract class class P { public: virtual ~P() = 0; virtual P  *clone() const = 0; }; template  P::~P() {} template  P * P :: clone() const { return new P  ( *this ); } template  //Abstract class class L { public: virtual ~L() = 0; virtual L  *clone() const = 0; }; template  L::~L() {} template  L *L  ::clone() const { return new L  ( *this );} template  class PL: virtual public P , virtual public L  //Abstract class { public: PL() : P (), L() {} virtual ~PL() = 0; virtual PL  *clone() const = 0; }; template  PL::~PL() {} template  PL * PL  :: clone() const { return new PL  ( *this );} template  class PC : virtual public P  { protected: T pc; public: PC() : P  () {} virtual ~PC() {} virtual PC  *clone() const {return new PC  ( *this );} }; template  class PA : virtual public P  { public: PA() : P  () {} virtual ~PA() {} virtual PA  *clone() const {return new PA  ( *this );} }; template  class PCL : public PC , public PL  { public: PCL() : P  (), PC  (), PL  () {} virtual ~PCL() {} virtual PCL  *clone() const {return new PCL  ( *this );} }; //Error using VS 2010: Error 1 error C2250: 'PCL' : ambiguous inheritance of 'PC *P::clone(void) const' template  class PAL : public PA , public PL  { public: PAL() : P  (), PA  (), PL  () {} virtual ~PAL() {} virtual PAL  *clone() const {return new PAL  ( *this );} }; //Error VS 2010: Error 1 error C2250: 'PAL' : ambiguous inheritance of 'PA *P::clone(void) const' int main(int argc, char* argv[]) { PCL  * pcl = new PCL (); PAL  * pal = new PAL (); PL  *pl1 = pcl->clone(); PL  *pl2 = pal->clone(); return 0; } 

J’ai peut-être oublié quelque chose … Mais g ++ comstack ce code correctement.

Cela comstack pour moi avec VC10:

 template  //Abstract class class P { public: virtual ~P() = 0; virtual P  *clone() const = 0; }; template  P::~P() {} template  //Abstract class class L { public: virtual ~L() = 0; virtual L  *clone() const = 0; }; template  L::~L() {} template  class PL: virtual public P , virtual public L  //Abstract class { public: PL() : P (), L() {} virtual ~PL() = 0; // virtual PL  *clone() const = 0; // REMOVED! }; template  PL::~PL() {} template  class PC : virtual public P  { protected: T pc; public: PC() : P  () {} virtual ~PC() {} virtual PC  *clone() const {return new PC  ( *this );} }; template  class PA : virtual public P  { public: PA() : P  () {} virtual ~PA() {} virtual PA  *clone() const {return new PA  ( *this );} }; template  class PCL : public PC , public PL  { public: PCL() : P  (), PC  (), PL  () {} virtual ~PCL() {} virtual PCL  *clone() const {return new PCL  ( *this );} }; template  class PAL : public PA , public PL  { public: PAL() : P  (), PA  (), PL  () {} virtual ~PAL() {} virtual PAL  *clone() const {return new PAL  ( *this );} }; int main() { PCL  * pcl = new PCL (); PAL  * pal = new PAL (); PL  *pl1 = pcl->clone(); PL  *pl2 = pal->clone(); return 0; } 

Notez que je devais supprimer PL *PL ::clone() pour que VC accepte le code. Le problème avec cela est que, si vous avez un PL et appelez son clone() , il retournera statiquement un L* , plutôt qu’un PL* , même si le type dynamic est de une classe dérivée de PL .

Juste quelques petites erreurs dans votre code:

  • Obtenez les initialiseurs de base corrects: PAL() : PC (), PL () {} Il n’y a pas d’initialisation de P , qui n’est pas une base directe; mais [Désolé, c’était faux – vous devez appeler le constructeur de la base virtuelle à cause de l’inheritance virtuel.] Vous devez dire les parenthèses rondes.

  • Déclarez des fonctions virtuelles pures sans définition: virtual L *clone() const = 0;

  • Vérifiez à nouveau que vous voulez que PL hérite virtuellement de P mais pas virtuellement de L (mais c’est correct).

  • Vérifiez à nouveau que tous vos clone() s ont la même constance.

  • Si vous avez déjà une fonction virtuelle pure dans votre classe de base abstraite, vous ne devez pas rendre le destructeur pur. Par exemple, virtual ~T() { } . Sinon, vous devriez toujours fournir une définition du destructeur virtuel pur (car le destructeur doit toujours être appelable): virtual T::~T() { }

MSVC ne prend pas correctement en charge les retours de co-variantes. Lorsque vous pensez que vous avez outrepassé la fonction, vous la cachez simplement. C’est le problème qui se pose.

Parce que la méthode de clonage dans votre classe P est abstraite telle que définie

 virtual P  *clone() const = 0 {}; 

(d’ailleurs que {} est incorrect). Ce que vous devez comprendre, c’est qu’une fois ce modèle instancié, il s’agit d’une méthode séparée avec une signature complètement différente de la méthode de dérivation de classes. L’instanciation de modèle crée se comporte comme si elle générait un nouveau code. Il s’agit donc d’une méthode abstraite pure qui n’a jamais été mise en œuvre. Ainsi, tous ceux qui héritent de cette méthode (chaque classe dérivée) deviennent une classe abstraite.


Edit: Comme pour la troisième question. Le polymorphism des temps d’exécution et de compilation ne se mélange pas bien. Je ne sais pas pourquoi vous voulez utiliser une structure aussi complexe, mais je suis sûr que la conception peut être simplifiée beaucoup plus.

  1. Il existe quelques fautes de frappe qui doivent être corrigées dans votre code, mais les s virtual dans le réseau d’inheritance sont corrects. (Ils sont le minimum. Vous pouvez en append davantage; dans les rares cas où de tels réseaux se produisent, il est souvent préférable de rendre tous les inheritances virtuels, pour se protéger contre les évolutions futures.)

  2. Il n’y a rien de mal avec votre code de test. Une fois que les fautes de frappe dans l’original sont corrigées, la compilation se passe bien avec g ++.

  3. C’est possible, et cela devrait fonctionner comme vous l’avez fait.