引言:
虚基类一般用来解决类多继承中的二义性问题的。比如C所继承的A和B都继承了base,那么在A和B中对父类base都加一个virtual 关键字,那么就可以避免构造C的时候构造两次base。
但是!自己用本来用的好好的啥事也没有,但是考试就会出一些代码让你猜猜,不是,让你写出程序的运行结果。
举个例子:(例子有点变态因为是我自己随便写的)
#include <iostream>
using namespace std;
class A {
public:
A()
{
cout << "this is A class!n";
}
};
class B {
public:
B()
{
cout << "this is B class!n";
}
};
class base1
{
public:
base1()
{
cout << "this is base1 class!n";
}
};
class base2:virtual public A
{
public:
base2()
{
cout << "this is base2 class!n";
}
};
class base3 :public B
{
public:
base3()
{
cout << "this is base3 class!n";
}
};
class base4 : virtual public A
{
public:
base4()
{
cout << "this is base4 class!n";
}
};
class level1 : public base1, virtual public base2
{
public:
level1()
{
cout << "this is level1 class!n";
}
};
class level2 :virtual public base3, public base4
{
public:
level2()
{
cout << "this is level2 class!n";
}
};
class toplevel : public level1, virtual public level2
{
public:
toplevel()
{
cout << "this is toplevel class!n";
}
};
void main()
{
toplevel t;
}
来,请你手写出运行结果(摊手)
。
。
。
。
。
。
。
结果是:(vs2019)
废话不多讲了,直接讲方法了(注意这个规律是我运行大量例子总结而来,并不具有权威指导性,若有不对还请指正)
首先
将整个继承结构看成一个树,把各个类看成节点:
把它倒过来就和平时做的二叉树类似,其中toplevel是整棵树的根节点。
然后我们以如下规则遍历这个树:
对根节点进行操作α
操作α:(注意这整个操作我们将其称为操作α,下面会递归使用)
设当前节点为root,
对以root为根节点的树进行第一次遍历(深度优先搜索)
若遇到virtual节点,则对之进行操作α
第一次遍历完后,
则对root进行第二次遍历,
遇到未构造过的节点(设为x),则执行x的构造函数。
第二次遍历完后,
已经可以保证root为根的树的所有节点均已执行过构造函数,
此时执行root的构造函数(注意如果root是virtual且与其同名类的构造函数已经执行过则不执行)
操作α至此结束。
下面来把这个规则在上面的例子上走一遍:
对toplevel执行操作α
第一次遍历:
第一个找到的virtual是base2
对base2进行操作α
对base2第一次遍历:
第一个找到的virtual是A
对A进行操作α
(因为A是叶子节点,两次遍历直接结束)
A是virtual且没有构造过,执行A的构造函数 1
对A操作α结束
第一次遍历结束
对base2第二次遍历
第二次遍历结束
base2是virtual且没有构造过,执行base2的构造函数 2
对base2操作α结束
第二个找到的是level2(注意A不再去遍历了,因为base2的子树都已构造好了)
对level2进行操作α
对level2进行第一次遍历
第一个找到的virtual是base3
对base3进行操作α
对base3第一次遍历
第一次遍历结束
对base3第二次遍历
遇到B未被构造,执行B构造函数 3
第二次遍历结束
base3是virtual且没有被构造过,执行base3构造函数 4
对base3操作结束
第二个找到的virtual是A
对A进行操作α
第A两次遍历结束
因为A是virtual且已经被构造过,所以不执行构造函数(这里解决了二义性问题)
对A操作结束
第一次遍历结束
对level2第二次遍历
base4未构造过,执行base4构造函数 5
第二次遍历结束
level2是virtual且未被构造过,执行level2构造函数 6
对level2操作结束
toplevel第一次遍历结束
toplevel第二次遍历开始
执行base1构造函数 7
执行level1构造函数 8
toplevel第二次遍历结束
因为toplevel未被构造过,执行toplevel构造函数 9
操作结束
以上数字标注部分起来就是最后的执行结果。
总之起来就是两次遍历,第一次遍历先构造virtual,同时保证所构造的virtual节点的所有子节点都完成了构造,第二次遍历构造剩下的部分非virtual节点。可以结合图慢慢体会~
(由于时间原因本文没空慢慢打磨,操作α写的也不是很精简(本来想用代码表示但是发现那样看起来简洁但是更加难解释),还望见谅)
(文中难免会有细节错误,欢迎批评指正)
(最后强调,本文只是根据经验总结出的规律,并不具有权威性和绝对正确性,有懂得大佬欢迎指教)
原文链接: https://www.cnblogs.com/fighlone/p/15768702.html
欢迎关注
微信关注下方公众号,第一时间获取干货硬货;公众号内回复【pdf】免费获取数百本计算机经典书籍
原创文章受到原创版权保护。转载请注明出处:https://www.ccppcoding.com/archives/183540
非原创文章文中已经注明原地址,如有侵权,联系删除
关注公众号【高性能架构探索】,第一时间获取最新文章
转载文章受原作者版权保护。转载请注明原作者出处!