In depth analysis of multiple inheritance of classes in C++

  • 2020-05-05 11:35:14
  • OfStack

Multiple inheritance of C++ class
In the previous examples, derived classes have only one base class, called single inheritance. In addition, C++ also supports multiple inheritance, meaning that a derived class can have two or more base classes.
Multiple inheritance is easy to make the code logic complex and confusing, which has been controversial. It is seldom used in small and medium-sized projects. Later, Java, C#, PHP and others simply canceled multiple inheritance. Readers who want to learn C++ quickly can do so without reading closely.
The multi-inheritance syntax is also simple, separating multiple base classes by commas. For example, if the classes A, B, and C have been declared, the derived class D:
can be declared as follows


class D: public A, private B, protected C{
 // class D New members 
}


D is a multi-inheritance derived class that inherits the A class in a common way, the B class in a private way, and the C class in a protected way. D gets members of A, B, C according to different inheritance methods, and determines the access rights of members of each base class in the derived class.
Constructor

under multiple inheritance

The constructor for a multi-inherited derived class is essentially the same as for a single-inherited class, except that it contains multiple base class constructors. Such as:


D Class constructor name ( The total number is listed in the table ): A The constructor ( The argument list ), B Class constructor ( The argument list ), C Class constructor ( The argument list ){
  New member initialization statement 
}


The order of the base classes is arbitrary.

The order of execution of derived class constructors is the same: the constructor of the base class is called first, then the derived class constructor is called. The base class constructor is called in the order in which the base class appears when the derived class is declared.

The following defines two base classes, BaseA and BaseB, and then derives the Sub class by multiple inheritance.


#include <iostream>
using namespace std;
// The base class 
class BaseA{
protected:
 int a;
 int b;
public:
 BaseA(int, int);
};
BaseA::BaseA(int a, int b): a(a), b(b){}
// The base class 
class BaseB{
protected:
 int c;
 int d;
public:
 BaseB(int, int);
};
BaseB::BaseB(int c, int d): c(c), d(d){}
// Derived classes 
class Sub: public BaseA, public BaseB{
private:
 int e;
public:
 Sub(int, int, int, int, int);
 void display();
};
Sub::Sub(int a, int b, int c, int d, int e): BaseA(a, b), BaseB(c, d), e(e){}
void Sub::display(){
 cout<<"a="<<a<<endl;
 cout<<"b="<<b<<endl;
 cout<<"c="<<c<<endl;
 cout<<"d="<<d<<endl;
 cout<<"e="<<e<<endl;
}
int main(){
 (new Sub(1, 2, 3, 4, 5)) -> display();
 return 0;
}

Result:


a=1
b=2
c=3
d=4
e=5

Member variables inherited from the base classes BaseA and BaseB are accessible in Sub::display().
name conflict

When two base classes have a member with the same name, a name conflict occurs and the member cannot be accessed directly. You need to add the class name and the domain resolver.

If there is a member function display() in both the base classes BaseA and BaseB, the following statement is wrong:


Sub obj;
obj.display();


Since both BaseA and BaseB have display(), the system will not be able to determine which class's function to call, so it reports an error.

The class name and the domain resolver should be added as follows:


Sub obj;
obj.BaseA::display();
obj.BaseB::display();


From this example, you can see that in multiple inheritance, duplicate data is inherited from different base classes. The problem is more pronounced if you have more than one base class, so consider its data members carefully when designing derived classes to minimize data redundancy.

The ambiguity problem of C++ multiple inheritance
Multiple inheritance can reflect the situation in real life, can effectively deal with some complex problems, so that the writing of the program has the flexibility, but multiple inheritance also caused some noteworthy problems, it increases the complexity of the program, make the program writing and maintenance become relatively difficult, prone to errors. One of the most common problems is the ambiguity (ambiguous) of an inherited member with the same name.

If both classes A and B have member functions display and data member a, class C is a direct derivative of classes A and B. Discuss the following three cases respectively.

1) two base classes have a member

with the same name

The code looks like this:


class A
{
public:
 int a;
 void display();
};
class B
{
public:
 int a;
 void display ();
};
class C: public A, public B
{
public:
 int b ; 
 void show();
};

If you define the C class object cl in the main function and call the data member a and the member function display:


 C cl;
 cl.a=3;
 cl.display();


Since both the base class A and the base class B have data members a and the member function display, the compilation system cannot tell which base class member is being accessed, so the program compilation fails. So how to solve this problem? You can qualify it with the base class name:


 cl.A::a=3; // reference cl The base class in the object A Data member of a
 cl.A::display(); // call cl The base class in the object A Member function of display

If you are accessing display and a of the base class A in the derived class C through the derived class member function show, you can write
directly without having to write the object name


 A::a = 3; // Current object 
 A::display();

2) the two base classes and derived classes all have members with the same name,

Change the C class declaration above to:


D Class constructor name ( The total number is listed in the table ): A The constructor ( The argument list ), B Class constructor ( The argument list ), C Class constructor ( The argument list ){
  New member initialization statement 
}
0


If you define the C class object cl in the main function and call the data member a and the member function display:


D Class constructor name ( The total number is listed in the table ): A The constructor ( The argument list ), B Class constructor ( The argument list ), C Class constructor ( The argument list ){
  New member initialization statement 
}
1


At this point, the program can be compiled or run properly. Which member of a class is accessed at execution time? The answer is: access is a member of the derived class C. The rule is that the members of a base class with the same name are masked in a derived class and become "invisible", or that the newly added members of a derived class with the same name override the members of the base class with the same name. So if a member with the same name is accessed through the object name in the module where the derived class object is defined, the member of the derived class is accessed. Please note: different member functions, only in the name of the function and the number of parameters are the same, the type of the case of the same name coverage, if only the function name is the same and the parameters are different, the same name coverage will not occur, but belongs to the function overload.

Some readers may be confused by the coverage of the same name. To illustrate, for example, with China as the base class, sichuan is a derived class of China, and chengdu is a derived class of sichuan. The base class is relatively abstract, the derived class is relatively concrete, the base class is in the outer layer, has a wider scope, the derived class is in the inner layer, has the local scope. If "China" has the property of average temperature, sichuan and chengdu also have the property of average temperature. If there are no two derived classes of sichuan and chengdu, talking about average temperature obviously refers to the national average temperature. If you are in sichuan, talking about the local average temperature obviously means the average temperature in sichuan; If you are in chengdu, talking about the local average temperature obviously means the average temperature in chengdu. That is to say, the national "average temperature" in sichuan province is shielded by the sichuan "average temperature", or the sichuan "average temperature" in the local shield of the national "average temperature". Sichuan people are most concerned about the temperature in sichuan, of course, do not want to use the national temperature to cover the average temperature in sichuan.

If you want to check the national average temperature in sichuan, be sure to state: I want to check the national average temperature. Similarly, to access a member of the base class A outside of a derived class, the scope A should be specified as


D Class constructor name ( The total number is listed in the table ): A The constructor ( The argument list ), B Class constructor ( The argument list ), C Class constructor ( The argument list ){
  New member initialization statement 
}
2

3) class A and class B are derived from the same base class,

The code looks like this:


class N
{
public:
 int a ; 
 void display(){ cout<<"A::a="<<a<<endl; }
};
class A: public N
{
public:
 int al;
};
class B: public N
{
public:
 int a2;
};
class C: public A, public B
{
public:
 int a3;
 void show(){ cout<<"a3="<<a3<<endl; }
}
int main()
{
 C cl; // define C Class object cl
 //  Other code 
}

Although class A and class B do not define data member a and member function display, they inherit data member a and member function display respectively from class N, so that two data member a and member function display with the same name exist simultaneously in class A and class B. They are copies of the N class members. The data member a in class A and class B represent two different storage units that can hold different data separately. In the program, the constructor of the base class N can be called through the constructors of A and B, respectively, to initialize the data member a of A and B.

How do I access the members of class A that inherit from the base class N? You can't use


D Class constructor name ( The total number is listed in the table ): A The constructor ( The argument list ), B Class constructor ( The argument list ), C Class constructor ( The argument list ){
  New member initialization statement 
}
4


Or


D Class constructor name ( The total number is listed in the table ): A The constructor ( The argument list ), B Class constructor ( The argument list ), C Class constructor ( The argument list ){
  New member initialization statement 
}
5


It is still impossible to tell whether a member of class A inherited from the base class N or a member of class B inherited from the base class N. The base class member in which class N is to be accessed should be indicated by the directly derived class name of class N. Such as


D Class constructor name ( The total number is listed in the table ): A The constructor ( The argument list ), B Class constructor ( The argument list ), C Class constructor ( The argument list ){
  New member initialization statement 
}
6


Related articles: