How to inherit several classes, which in turn are inherited from another class? Picture for clarity. At the moment I have written only 4 classes (Point, Line, Circle, Animation), but this is already enough for errors in the compiler. Compile errors:

In file included from main.cpp:6:0: animation.cpp: In member function 'void ClassAnimation::set_shape_for_animation(char*)': animation.cpp:49:34: error: 'ClassPoint' is an ambiguous base of 'ClassAnimation' coordX = ClassPoint::coordX; ^~~~~~ animation.cpp:50:34: error: 'ClassPoint' is an ambiguous base of 'ClassAnimation' coordY = ClassPoint::coordY; ^~~~~~ animation.cpp:51:39: error: 'ClassPoint' is an ambiguous base of 'ClassAnimation' width_shape = ClassPoint::width_size; ^~~~~~~~~~ animation.cpp:52:40: error: 'ClassPoint' is an ambiguous base of 'ClassAnimation' height_shape = ClassPoint::height_size; ^~~~~~~~~~~ animation.cpp:59:40: error: 'ClassPoint' is an ambiguous base of 'ClassAnimation' height_shape = ClassPoint::height_size; ^~~~~~~~~~~ animation.cpp: In member function 'void ClassAnimation::shape_paint()': animation.cpp:84:37: error: 'ClassPoint' is an ambiguous base of 'ClassAnimation' ClassPoint::point_paint(); ^ animation.cpp: In member function 'void ClassAnimation::shape_clear()': animation.cpp:101:37: error: 'ClassPoint' is an ambiguous base of 'ClassAnimation' ClassPoint::point_clear(); ^ main.cpp: In function 'int main()': main.cpp:46:27: error: request for member 'set_point_height' is ambiguous obj_point.set_point_height(set_parameter); ^~~~~~~~~~~~~~~~ In file included from main.cpp:3:0: point_class.cpp:29:10: note: candidates are: void ClassPoint::set_point_height(int) void set_point_height(int set_height) ^~~~~~~~~~~~~~~~ point_class.cpp:29:10: note: void ClassPoint::set_point_height(int) main.cpp:50:27: error: request for member 'set_point_width' is ambiguous obj_point.set_point_width(set_parameter); ^~~~~~~~~~~~~~~ In file included from main.cpp:3:0: point_class.cpp:35:10: note: candidates are: void ClassPoint::set_point_width(int) void set_point_width(int set_width) ^~~~~~~~~~~~~~~ point_class.cpp:35:10: note: void ClassPoint::set_point_width(int) main.cpp:54:27: error: request for member 'set_point_coordX' is ambiguous obj_point.set_point_coordX(set_parameter); ^~~~~~~~~~~~~~~~ In file included from main.cpp:3:0: point_class.cpp:41:10: note: candidates are: void ClassPoint::set_point_coordX(const float&) void set_point_coordX(const float &set_x) ^~~~~~~~~~~~~~~~ point_class.cpp:41:10: note: void ClassPoint::set_point_coordX(const float&) main.cpp:58:27: error: request for member 'set_point_coordY' is ambiguous obj_point.set_point_coordY(set_parameter); ^~~~~~~~~~~~~~~~ In file included from main.cpp:3:0: point_class.cpp:57:10: note: candidates are: void ClassPoint::set_point_coordY(const float&) void set_point_coordY(const float &set_y) ^~~~~~~~~~~~~~~~ point_class.cpp:57:10: note: void ClassPoint::set_point_coordY(const float&) main.cpp:62:27: error: request for member 'set_point_color' is ambiguous obj_point.set_point_color(set_parameter); ^~~~~~~~~~~~~~~ In file included from main.cpp:3:0: point_class.cpp:73:10: note: candidates are: void ClassPoint::set_point_color(int) void set_point_color(int set_color) ^~~~~~~~~~~~~~~ point_class.cpp:73:10: note: void ClassPoint::set_point_color(int) main.cpp:64:27: error: request for member 'point_paint' is ambiguous obj_point.point_paint(); ^~~~~~~~~~~ In file included from main.cpp:3:0: point_class.cpp:79:10: note: candidates are: void ClassPoint::point_paint() void point_paint() ^~~~~~~~~~~ point_class.cpp:79:10: note: void ClassPoint::point_paint() [Finished in 1.2s] 


If you inherit from the ClassAnimation class only one of the Line or Circle classes, the program works correctly and the compiler does not complain. Otherwise, if you transmit all two classes, it starts complaining.

Classpoint

 class ClassPoint { protected: float *coordX = NULL; float *coordY = NULL; int height_size; int width_size; int color_point; public: ClassPoint() { } ~ClassPoint() { delete coordX; delete coordY; } //Выбор высоты точки void set_point_height(int set_height) { height_size = set_height; } //Выбор ширины точки void set_point_width(int set_width) { width_size = set_width; } //Выбор координаты X точки void set_point_coordX(const float &set_x) { if (coordX == NULL) { coordX = new float; } else { delete coordX; coordX = new float; } *coordX = set_x; } //Выбор координаты Y точки void set_point_coordY(const float &set_y) { if (coordY == NULL) { coordY = new float; } else { delete coordY; coordY = new float; } *coordY = set_y; } //Выбор цвета точки void set_point_color(int set_color) { color_point = set_color; } //Нарисовать точку void point_paint() { setfillstyle(1, color_point); int x1 = *coordX - width_size/2; int y1 = *coordY - height_size/2; int x2 = *coordX + width_size/2; int y2 = *coordY + height_size/2; bar(x1, y1, x2, y2); } //Стереть точку void point_clear() { setfillstyle(1, 15); int x1 = *coordX - width_size/2; int y1 = *coordY - height_size/2; int x2 = *coordX + width_size/2; int y2 = *coordY + height_size/2; bar(x1, y1, x2, y2); } }; 

ClassLine

 class ClassLine : public ClassPoint { protected: float *coordX = NULL; float *coordY = NULL; int length_line; public: ClassLine() { } ~ClassLine() { delete coordX; delete coordY; } //Выбрать толщину прямой void set_line_width(int set_width) { ClassPoint::height_size = set_width; ClassPoint::width_size = set_width; } //Выбрать длину прямой void set_line_length(int set_length) { length_line = set_length; } //Выбрать координату центра прямой по X void set_line_coordX(int set_x) { if (coordX == NULL) { coordX = new float; } else { delete coordX; coordX = new float; } *coordX = set_x; ClassPoint::coordX = coordX; } //Выбрать координату центра прямой по Y void set_line_coordY(int set_y) { if (coordY == NULL) { coordY = new float; } else { delete coordY; coordY = new float; } *coordY = set_y; ClassPoint::coordY = coordY; } //Выбрать цвет прямой void set_line_color(int set_color) { ClassPoint::color_point = set_color; } //Нарисовать прямую void line_paint() { int end_paint = length_line/2; float position = 1; ClassPoint::coordX = coordX; int to_right_paint = 0; while (to_right_paint < end_paint) { *(ClassPoint::coordX) += position; ClassPoint::point_paint(); to_right_paint++; } ClassPoint::coordX = coordX; int to_left_paint = 0; while (to_left_paint < end_paint) { *(ClassPoint::coordX) -= position; ClassPoint::point_paint(); to_left_paint++; } } //Стереть прямую void line_clear() { int end_clear = length_line/2; float position = 1; ClassPoint::coordX = coordX; int to_right_clear = 0; while (to_right_clear < end_clear) { *(ClassPoint::coordX) += position; ClassPoint::point_clear(); to_right_clear++; } ClassPoint::coordX = coordX; int to_left_clear = 0; while (to_left_clear < end_clear) { *(ClassPoint::coordX) -= position; ClassPoint::point_clear(); to_left_clear++; } } }; 

Classcircle

 class ClassCircle : public ClassPoint { protected: int radius_circle; float *coordX = NULL; float *coordY = NULL; public: ClassCircle() { } ~ClassCircle() { delete coordX; delete coordY; } //Выбрать радиус окружности void set_circle_radius(int set) { radius_circle = set; } //Выбрать толщину контура окружности void set_circle_width_line(int set) { ClassPoint::height_size = set; ClassPoint::width_size = set; } //Выбрать координату центра по Х void set_circle_coordX(float set_x) { if (coordX == NULL) { coordX = new float; } else { delete coordX; coordX = new float; } *coordX = set_x; ClassPoint::coordX = coordX; } //Выбрать координату центра по Y void set_circle_coordY(float set_y) { if (coordY == NULL) { coordY = new float; } else { delete coordY; coordY = new float; } *coordY = set_y; ClassPoint::coordY = coordY; } //Выбрать цвет окружности void set_circle_color(int set) { ClassPoint::color_point = set; } //Нарисовать окружность void circle_paint() { float position = 0.1; float start_circle = *coordX - radius_circle; float end_circle = *coordX + radius_circle; float x = *(ClassPoint::coordX) - position; ClassPoint::coordX = &x; while (*(ClassPoint::coordX) < end_circle) { ClassPoint::point_paint(); float a = -sqrt(pow(radius_circle, 2) - pow((*(ClassPoint::coordX) - *coordX), 2)) + *coordY; ClassPoint::coordY = &a; float b = *(ClassPoint::coordX) + position; ClassPoint::coordX = &b; } while (*(ClassPoint::coordX) > start_circle) { ClassPoint::point_paint(); float a = sqrt(pow(radius_circle, 2) - pow((*(ClassPoint::coordX) - *coordX), 2)) + *coordY; ClassPoint::coordY = &a; float b = *(ClassPoint::coordX) - position; ClassPoint::coordX = &b; } } //Стереть окружность void circle_clear() { float x1 = *coordX - radius_circle - 1; float y1 = *coordY - radius_circle - 1; float x2 = *coordX + radius_circle + 1; float y2 = *coordY + radius_circle + 1; setfillstyle(1, 15); bar(x1, y1, x2, y2); } }; 

ClassAnimation

 class ClassAnimation : public ClassLine, public ClassCircle { protected: int border_left; int border_top; int border_right; int border_bottom; int min_coordX; int max_coordX; int min_coordY; int max_coordY; float *coordX = NULL; float *coordY = NULL; int type_direction; char type_shape[20]; int width_shape; int height_shape; public: ClassAnimation() { border_left = 25; border_top = 25; border_right = 25 + 250; border_bottom = 25 + 250; } ~ClassAnimation() { delete coordX; delete coordY; } //Выбор фигуры для анимации движения void set_shape_for_animation(char *shape_name) { strcpy(type_shape, shape_name); if (strcmp(type_shape, "point") == 0) { coordX = ClassPoint::coordX; coordY = ClassPoint::coordY; width_shape = ClassPoint::width_size; height_shape = ClassPoint::height_size; } else if (strcmp(shape_name, "line") == 0) { coordX = ClassLine::coordX; coordY = ClassLine::coordY; width_shape = ClassLine::length_line; height_shape = ClassPoint::height_size; } else if (strcmp(shape_name, "circle") == 0) { coordX = ClassCircle::coordX; coordY = ClassCircle::coordY; width_shape = ClassCircle::radius_circle * 2; height_shape = ClassCircle::radius_circle * 2; } } //Границы анимации точки void border_animation_point() { min_coordX = border_left + (width_shape/2); max_coordX = border_right - (width_shape/2); min_coordY = border_top + (height_shape/2); max_coordY = border_bottom - (height_shape/2); } //Рисование фигуры void shape_paint() { if (strcmp(type_shape, "point") == 0) { ClassPoint::point_paint(); } else if (strcmp(type_shape, "line") == 0) { ClassLine::line_paint(); } else if (strcmp(type_shape, "circle") == 0) { ClassCircle::circle_paint(); } } //Стирание фигуры void shape_clear() { if (strcmp(type_shape, "point") == 0) { ClassPoint::point_clear(); } else if (strcmp(type_shape, "line") == 0) { ClassLine::line_clear(); } else if (strcmp(type_shape, "circle") == 0) { ClassCircle::circle_clear(); } } //Выбор случайного направления движения void set_random_direction() { type_direction = rand()%4; } /*Смещение фигуры в зависимости от направления 1 0 2 3*/ //Движение точки вверх вправо void direction_up_right() { if (*coordX >= max_coordX) { type_direction = 1; return; } else if (*coordY <= min_coordY) { type_direction = 3; return; } else if (type_direction == 0) { shape_clear(); *coordX += 1; *coordY -= 1; shape_paint(); } } //Движение точки вверх влево void direction_up_left() { if (*coordX <= min_coordX) { type_direction = 0; return; } else if (*coordY <= min_coordY) { type_direction = 2; return; } else if (type_direction == 1) { shape_clear(); *coordX -= 1; *coordY -= 1; shape_paint(); } } //Движение точки вниз вправо void direction_down_right() { if (*coordX >= max_coordX) { type_direction = 2; return; } else if (*coordY >= max_coordY) { type_direction = 0; return; } else if (type_direction == 3) { shape_clear(); *coordX += 1; *coordY += 1; shape_paint(); } } //Движение точки вниз влево void direction_down_left() { if (*coordX <= min_coordX) { type_direction = 3; return; } else if (*coordY >= max_coordY) { type_direction = 1; return; } else if (type_direction == 2) { shape_clear(); *coordX -= 1; *coordY += 1; shape_paint(); } } void start_animation_motion() { switch (type_direction) { case 0: direction_up_right(); break; case 1: direction_up_left(); break; case 2: direction_down_left(); break; case 3: direction_down_right(); break; } } }; 

P.S. I work with the outdated graphics.h library. I have not studied visual programming yet, and c ++ has recently begun to study. Prior to that, I studied only structured C programming.

  • Try to provide a minimal reproducible example . Appreciate not only your time, but also the time of other participants. - αλεχολυτ
  • In my other question, a person who helps me understand another problem has asked for a specific program code in order to better understand what is happening with me. Now they tell me to minimize the code based on a simple example. That's just the point is not the size of the code. With empty classes A, B, C and D, everything works fine for me. So I see no reason to show an example that works in this way. I am not forcing you to waste your time on me. Everyone will find the value of their time and decide for themselves what to spend and how much. - Code
  • See virtual inheritance, in this case I think this is exactly what you need. - Vladimir Pavluk

3 answers 3

Multiple inheritance is actually a very difficult and unpleasant topic. In short, you get several instances of the base class, and the compiler simply does not understand which one is the most :) the base class is meant. There are two solutions - depending on what you need. Either contact the members of this ambiguous base class with an indication of the intermediate class, or declare it a virtual base class.

Option 1:

 class A { int a; public: A(int a):a(a){} void out() { cout << "A: "<< a << endl; } }; class B: public A { public: B(int a):A(a){} }; class C: public A { public: C(int a):A(a){} }; class D: public B, public C { public: D(int a):B(a),C(a){} void out() { B::out(); } }; 

In D::out() we call out() the instance A , which is the ancestor of B

Option 2:

 class A { int a; public: A(int a):a(a){} void out() { cout << "A: "<< a << endl; } }; class B: virtual public A { public: B(int a):A(a){} }; class C: virtual public A { public: C(int a):A(a){} }; class D: public B, public C { public: D(int a):A(a),B(a),C(a){} }; int main(int argc, const char * argv[]) { D d(5); d.out(); } 

Now A only one, and you can apply without ambiguities ...

There is a mass of subtleties - in the second version, in constructor D you must call constructor A , for example ... In a word, read the textbooks.

  • The school says I use static methods. Those. if I understand correctly: the second option will not work for me. Regarding the first option, this line A(int a):a(a){} not quite clear A(int a):a(a){} , can you explain? In classes B, C, and D, as I understand this is constructor inheritance, right? - Code
  • Well, why, it may well come up. I just think that based on the logic of the problem, the second option suits you better. A(inta):a(a){} - I just added this constructor. Initializes member a value of the a parameter passed to the constructor. - Harry
  • With virtual inheritance earned. The first option did not work and it didn’t seem to be like that, because it is identical to my original inheritance method, which did not work. The only thing that is not very clear is this: why should constructors inherit and constructors? Doesn't class inheritance mean that everything is inherited (variables, methods, constructors, destructors)? - Code
  • Each class has its own constructor, there is no need to talk about inheritance. Destructor too. They are not inherited, the constructor of the base class is simply called (unless you explicitly specify which one - the default constructor will be called). The same for destructor. By the way, since you have inheritance, make destructors virtual! But methods and variables are yes inherited. But even here there are subtleties (such as hiding names, etc.). Well, in the "non-working" first version you probably didn’t pay attention in `void out () {B :: out (); } `to B:: . - Harry
  • Yes, I did not notice this. Those. In option 1, you always need to specify the desired space for the method / variable? And if I need to refer to a class A method / variable in class D? How to correctly write this? - Code

The compiler generates error messages, because your classes usually have several base classes of ClassPoint , that is, several subobjects of type ClassPoint created in the ClassPoint , and calling a method or calling a data member of this class is ambiguous.

You should use virtual inheritance.

According to the class hierarchy scheme that you refer to in your question. then class declarations should look like this

 #include <iostream> class ClassPoint { }; class ClassEllipse : virtual public ClassPoint { }; class ClassCircle : virtual public ClassPoint { }; class ClassLine : virtual public ClassPoint { }; class ClassRectangle : virtual public ClassLine { }; class ClassRhomb : virtual public ClassLine { }; class Animation : public ClassEllipse, public ClassCircle, public ClassLine, public ClassRectangle, public ClassRhomb { }; int main() { return 0; } 

However, I do not see much point in the fact that all classes inherit the ClassPoint class. First, the ClassPoint class ClassPoint looks unnatural. What does the point width and height mean?

 class ClassPoint { protected: float *coordX = NULL; float *coordY = NULL; int height_size; ^^^^^^^^^^^^^^^^ int width_size; ^^^^^^^^^^^^^^^ //... 

Why axial coordinates are specified as pointers, and not just objects of type float ?

Why, for example, class ClassLine

 class ClassLine : public ClassPoint { protected: float *coordX = NULL; ^^^^^^^^^^^^^^^^^^^^^ float *coordY = NULL; ^^^^^^^^^^^^^^^^^^^^^ //... 

determines some of its incomprehensible coordinates, and does not use the ClassPoint class? What is the meaning of inheriting from ClassPoint ?

In my opinion, no class should inherit the ClassPoint class, but should use this class to define points of the shape, such as the coordinates of the center of a circle, or points of a rectangle.

For example, the definition of the class ClassCircle might look like this:

 class ClassCircle : public ClassPoint { protected: int radius_circle; ClassPoint center; //... 

At the same time, each figure should be able to draw itself. And the class ClassAnimation should not be inherited from these figures.

By the way, this is a good excuse to read about design patterns. I think that this model is already described in one of the templates. :)

  • The width and height of the point determine the size of the point, yes, I have it is not quite a point, but a rectangle or square. ClassLine has coordinates that indicate the center coordinates of the line. And the fact that all coordinates are pointers, then I need it for the ClassAnimation class in order to change the addresses of other coordinate pointers using one pointer. - Code
  • @ShitCode I think you are using the wrong inheritance scheme. If ClassPoint is some rectangle, then you could call it, for example, as a frame - Frame, and define in it those methods to which messages from the ClassAnimation class will be sent. - Vlad from Moscow
  • Point class is a point class I draw shapes with. Due to the fact that my capabilities are limited by the graphics.h library, I chose the drawing function bar, which works much faster than the circle function. No need to take the Point class as directly. The size of this point (square) is set by the user from the keyboard, if you choose a small size (3 pixels for example), then the circle and line will look quite decent. - Code
  • @ShitCode It's still not clear why other classes inherit this class. - Vlad from Moscow
  • Once again about the coordinates in each class will tell. If there is coordX and coordY in the class, it means that these are the coordinates of the center of the figure in which class they are described. Those. if the circle class has these two coordinates, it means that these are the coordinates of the center of the circle that will be needed in the Animation class. It turns out that depending on the shape that I want to start animating, I will STILL assign the corresponding addresses of this or that shape to the same (more general) coordinates in the Animation class. I did this in order not to write the motion algorithm for each shape. - Code

Probably because of this garbage in normal yap and abolished multiple inheritance. This is a bad structure. Either inherit from class "ClassPoint" class "ClassAnimation", and from him the rest. or do not inherit "ClassAnimation" at all, but do it as a separate tool (with animation for primitives ??).

Read here there is a solution to such jambs http://www.quizful.net/post/Inheritance-in-C++

And the problem is that the compiler does not know which implementation of the base class to include.