本文共 2870 字,大约阅读时间需要 9 分钟。
C++语言支持多继承,一个子类可以有多个父类,子类拥有所有父类的成员变量,子类继承所有父类的成员函数,子类对象可以当作任意父类对象使用。
class Derived : public BaseA, public BaseB, public BaseC {};
当两个父类存在相同名称的成员函数,在子类中,可以通过【类名::成员函数名称】的方式来确认调用的是哪个父类的函数。
多继承虽然很不错,但是有个致命的菱形继承问题,例如:
若此时在调用一个Doctor对象中,调用一个从People继承而来的方法时,将会编译错误,提示二义性。
#includeusing 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 继承方式 基类名
现在只需要在上面Teacher以及Student继承People的前面加上virtual关键字声明为虚继承,则可以解决上述二义性问题。
到目前为止,我们调用的都是各个类的默认构造函数,但是如果此时,我们想要调用其带参参数时,会如何呢,代码如下:
#includeusing 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是私有的,但仍会导致二义性。
转载地址:http://pwpub.baihongyu.com/