一、多继承

C++语言支持多继承,一个子类可以有多个父类,子类继承所有父类的public及protect成员变量及成员函数,任何可以使用父类的地方都可以使用子类替换。

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

};

当两个父类存在相同名称的成员函数,在子类中,可以通过【类名::成员函数名称】的方式来确认调用的是哪个父类的函数。

二、虚继承

多继承虽然很不错,但是有个致命的菱形继承问题,例如:

若此时在调用一个Doctor对象中,调用一个从People继承而来的方法时,将会编译错误,提示二义性。

#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;
}

执行结果如下:

为解决该类问题,C++提供了虚基类和虚继承机制,实现了在多继承中只保留一份共同成员,完美解决了菱形多继承导致的成员冗余问题。
虚继承的语法如下:
class 派生类名:virtual 继承方式 基类名

class Teacher : virtual public People {
public:

};


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


到目前为止,我们调用的都是各个类的默认构造函数,但是如果此时,我们想要调用其带参参数时,会如何呢,代码如下:

#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;
}

此时的运行结果是什么呢?此时的运行结果是:

可能你会问,明明在Teacher和Student类的初始化列表里面调用了People的带参构造函数,为什么没有得到调用呢,因为C++语言规定,在基类是虚的时候,将禁止信息通过中间类自动传递给基类,因此上述Teacher和Student类构造函数不会调用People的带参构造函数,而只会调用一次默认构造函数,但如果你不希望使用默认构造函数来构造虚基类对象,可以在子类中使用这样的初始化列表:

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

此时的运行结果为:

结论:如果类有虚基类,则除非确认只使用该虚基类的默认构造函数,否则必须显式地调用该虚基类的你一个构造函数。


三、虚基类和支配

我们知道,使用非虚基类时,如果子类从不同父类那里继承来了同名成员,如果我们调用时候不用类名进行限定,将导致二义性。但是如果此时是虚基类,且调用时候不加类名进行限定,也可能不会导致二义性,因为在某些情况下,如果某个类的成员优先于其他类的成员,则使用它时,也不会导致二义性。那优先级如何判断呢?派生类中的名称优先于直接或间接祖先类的相同名称。

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;
}

运行结果为:

若此时在Student中在定义个test函数,将会导致二义性,因为Teacher和Student优先级一样

另外,这个二义性和访问规则无关,即使Student中的test是私有的,但仍会导致二义性。