一、多继承
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是私有的,但仍会导致二义性。