博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
C++虚继承【详解】
阅读量:2194 次
发布时间:2019-05-02

本文共 2870 字,大约阅读时间需要 9 分钟。

一、多继承

C++语言支持多继承,一个子类可以有多个父类,子类拥有所有父类的成员变量,子类继承所有父类的成员函数,子类对象可以当作任意父类对象使用。

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

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

二、虚继承

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

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

#include 
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 继承方式 基类名

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

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

#include 
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是私有的,但仍会导致二义性。

 

 

 

 

转载地址:http://pwpub.baihongyu.com/

你可能感兴趣的文章
什么是 Q-learning
查看>>
用一个小游戏入门深度强化学习
查看>>
5 分钟入门 Google 最强NLP模型:BERT
查看>>
初探Java设计模式4:一文带你掌握JDK中的设计模式
查看>>
初探Java设计模式5:一文了解Spring涉及到的9种设计模式
查看>>
Java集合详解1:一文读懂ArrayList,Vector与Stack使用方法和实现原理
查看>>
Java集合详解2:一文读懂Queue和LinkedList
查看>>
Java集合详解3:一文读懂Iterator,fail-fast机制与比较器
查看>>
Java集合详解4:一文读懂HashMap和HashTable的区别以及常见面试题
查看>>
Java集合详解5:深入理解LinkedHashMap和LRU缓存
查看>>
Java集合详解6:这次,从头到尾带你解读Java中的红黑树
查看>>
Java集合详解7:一文搞清楚HashSet,TreeSet与LinkedHashSet的异同
查看>>
Java集合详解8:Java集合类细节精讲,细节决定成败
查看>>
Java并发指南1:并发基础与Java多线程
查看>>
Java并发指南2:深入理解Java内存模型JMM
查看>>
Java并发指南3:并发三大问题与volatile关键字,CAS操作
查看>>
Java并发指南4:Java中的锁 Lock和synchronized
查看>>
Java并发指南5:JMM中的final关键字解析
查看>>
Java并发指南6:Java内存模型JMM总结
查看>>
Java并发指南7:JUC的核心类AQS详解
查看>>