C ++, virtual inheritance, weird abstract class + clone issue

Sorry for the large amount of source code. There are three abstract classes P, L, PL. The third class PL is derived from classes P and L, using virtual inheritance:

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

Each class has its own implementation of the clone method.

The following two classes PA, PC are made from class P using virtual inheritance:

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

The last two classes PCL and PAL are duplicated using virtual inheritance from PC and PL, PA and PL.

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

};

There is a class dependency diagram:

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

Please do not discuss this proposal :-))). I have the following 3 questions:

1) Was this class dependency correctly rewritten in C ++ (first of all, the location of the "virtual")?

2) I'm not sure what is wrong, see the code, please:

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

It is not possible to create new objects of the PAL / PCL classes, both classes are marked as abstract. But they are not abstract. Where is the problem?

3) Can polymorphism be used with the clone () method? See code above, please ...

Thanks for your help...


UPDATE QUESTION

I have adjusted the code. But the following error appears using the VS 2010 compiler:

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

Maybe I missed something ... But g ++ compiles this code in order.

0
source share
5 answers

This is the compilation for me using VC10:

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

Note that I had to remove PL <T> *PL <T>::clone() in order to force VC to accept the code. The problem is that if you have PL<T> and clone() is called, it statically returns L<T>* , not PL<T>* , although the dynamic type has a class derived from PL<T> .

+1
source

Just some small errors in your code:

  • Get the initial initializers to the right: PAL() : PC <T>(), PL <T>() {} There is no initialization P<T> , which is not a direct base; but [Sorry, this was wrong - you need to call the virtual base constructor due to virtual inheritance.] you need to say parentheses.

  • Declare pure virtual functions without definition: virtual L <T> *clone() const = 0;

  • Double check that you want PL inherit almost from P , but not virtually from L (but that's fine).

  • Double check that all your clone() have the same constant.

  • If you already have one pure virtual function in your abstract base class, you should not make the destructor clean. Instead, say virtual ~T() { } . Otherwise, you should still provide a definition of a pure virtual destructor (since the destructor must always be callable): virtual T::~T() { }

+3
source

MSVC does not support the correct return of co-options. Where you think you are redefining a function, you were actually hiding it. This is problem.

+2
source

Since the clone method in your class P is abstract because it is defined

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

(btw that {} is wrong). You should understand that after creating this template, this is a separate method with a completely different signature from the clone method of getive classes. creating a template creates behavior as if it were generating new code. Thus, this is a pure abstract method that has never been implemented. Thus, everyone who inherits this method (each derived class) is an abstract class.


Edit: Regarding the third question. Runtime and compile-time polymorphism do not mix. I have no idea why on earth you want to use such a complex structure, but I am sure that the design can be simplified much more.

0
source
  • There are a few typos that need to be fixed in your code, but virtual in the inheritance grid is correct. (This is a minimum. You can add more; in rare cases where such gratings are often better to make all inheritance virtual in order to protect against future evolution.)

  • There is nothing wrong with your test code. After typos in the original fixed, it compiles fine with g ++.

  • It is possible, and it should work the way you did it.

0
source

Source: https://habr.com/ru/post/894230/


All Articles