C++ virtual inheritance [detailed]

One, multiple inheritance

C++ language supports multiple inheritance. A subclass can have multiple parent classes. The subclass has all the member variables of the parent class. The subclass inherits all the member functions of the parent class. The subclass object can be used as any parent class object.

class Derived : public BaseA,
                   public BaseB,
                   public BaseC   
{

};

When two parent classes have member functions with the same name, in the subclass, you can use [class name:: member function name] to confirm which parent class's function is called.

Second, virtual inheritance

Although multiple inheritance is great, there is a fatal diamond-shaped inheritance problem, such as:

If you call a method inherited from People in a Doctor object at this time, a compilation error will occur, prompting ambiguity.

#include <iostream>  

using namespace std;

class People {
public:
    void People_test() {
        cout << "People_test" << endl;
    }

};
class Teacher : public People {
public:

};
class Student : public People {
public:

};
class Doctor :public Teacher, public Student {
public:

};

int main()
{
    Doctor d;
    d.People_test();
    return 0;
}

The execution results are as follows:

At this time, a virtual inheritance is needed. C++ provides virtual base classes and virtual inheritance mechanisms to realize that only one common member is retained in multiple inheritance, which perfectly solves the problem of member redundancy caused by diamond multiple inheritance.
In virtual inheritance, the middle-level parent class no longer pays attention to the initialization of the top-level parent class, and the final child class must directly call the constructor of the top-level parent class.
The syntax of virtual inheritance is as follows:
class 派生类名:virtual 继承方式 基类名

现在只需要在上面Teacher以及Student继承People的前面加上virtual关键字声明为虚继承,则可以解决上述二义性问题。

So far, we have called the default constructor of each class, but if we want to call its parameters with parameters at this time, what will happen, the code is as follows:

#include <iostream>  

using namespace std;

class People {
public:
    void People_test() {
        cout << "People_test" << endl;
    }
    People(int a) {
        cout << "people have one param " << a << endl;
    }
    People() {
        cout << "people default" << endl;
    }
};
class Teacher :virtual public People {
public:
    Teacher(int a, int b) : People(a) {
        
    }
};
class Student :virtual public People {
public:
    Student(int a, int b) : People(a) {
        
    }
};
class Doctor :public Teacher, public Student {
public:
    Doctor(int a, int b) : Teacher(a, b), Student(a, b) {
     
    }
};

int main()
{
    Doctor d(1,2);
    
    return 0;
}

What is the result of the operation at this time? The result of the operation at this time is:

You may ask, why the parameterized constructor of People is called in the initialization list of the Teacher and Student classes. Why is it not called? Because the C++ language stipulates that when the base class is virtual, the prohibited information will be automatically passed through the intermediate class. Passed to the base class, so the above-mentioned Teacher and Student class constructors will not call People’s parameterized constructor, but will only call the default constructor once, but if you don’t want to use the default constructor to construct virtual base class objects, you can Use such an initialization list in the subclass:

Doctor(int a, int b) :People(b), Teacher(a, b), Student(a, b) {
     
    }

The running result at this time is:

Conclusion: If the class has a virtual base class, unless you only need to use the default constructor of the virtual base class, you must explicitly call a constructor of the virtual base class.

 

Three, virtual base class and dominance

We know that when using a non-virtual base class, if subclasses inherit members with the same name from different parent classes, if we do not use the class name to qualify when calling, it will lead to ambiguity. But if it is a virtual base class at this time, and the call is not qualified by the class name, it may not cause ambiguity, because in some cases, if the members of a certain class take precedence over the members of other classes, use When it does, it will not cause ambiguity. How to judge the priority? The name in the derived class takes precedence over the same name in the direct or indirect ancestor class.

class People {
public:
    void test() {
        cout << "People test" << endl;
    }
    People(int a) {
        cout << "people have one param " << a << endl;
    }
    People() {
        cout << "people default" << endl;
    }
};
class Teacher :virtual public People {
public:
    Teacher(int a, int b) : People(a) {
        
    }
    void test() {
        cout << "Teacher test" << endl;
    }
};
class Student :virtual public People {
public:
    Student(int a, int b) : People(a) {
        
    }
};
class Doctor :public Teacher, public Student {
public:
    Doctor(int a, int b) :People(b), Teacher(a, b), Student(a, b) {
     
    }
};

int main()
{
    Doctor d(1,2);
    d.test();
    return 0;
}

The results of the operation are:

If a test function is defined in Student at this time, it will cause ambiguity, because neither Teacher nor Student is the base class of each other.

In addition, this ambiguity has nothing to do with the access rules, even if the test in the Student is private, it still leads to ambiguity.

 

 

 

 

Guess you like

Origin blog.csdn.net/Chiang2018/article/details/113787190