虚基类——解决继承的菱形问题

在这里插入图片描述

1.如果按普通的继承方式,那么可以写出以下程序:

#include<iostream>
using namespace std;
class B {
protected:
	int b;
public:
	B(int bb = 0) :b(bb) { cout << "基类B的构造函数被调用" << endl; }
	void display() { cout << b << endl; }
};
class D1 : public B {
protected:
	int d1;
public:
	D1(int bb, int dd) :d1(dd), B(666) { cout << "D1类的构造函数被调用" << endl; }
	void display() { B::display(); }
};
class D2 : public B {
protected:
	int d2;
public:
	D2(int bb, int dd) :d2(dd), B(888) { cout << "D2类的构造函数被调用" << endl; }
	void display() { B::display(); }
};

class D3 :public D2, public D1 {
protected:
	int d3;
public:
	D3(int b, int d1, int d2, int d3) :d3(d3), D1(b, d1), D2(b, d2) { cout << "D3类的构造函数被调用" << endl; }
	void display() { D1::display(); D2::display(); }
};
int main() {
	D3 obj(1, 2, 3, 4);
	obj.display();

	system("pause");
	return 0;
}

创建D3类的对象obj时,我们可以发现obj中有两个b(显然,这是因为在执行D1类的构造函数和D2类的构造函数时,总共调用了2次基类的构造函数)。此时,就要用虚基类来解决这个问题。
在这里插入图片描述
在这里插入图片描述

2.程序改动如下:

#include<iostream>
using namespace std;
class B {
protected:
	int b;
public:
	B(int bb) :b(bb) { cout << "虚基类B的构造函数被调用" << endl; }
	void display() { cout << b << endl; }
};
class D1 :virtual public B {
protected:
	int d1;
public:
	D1(int bb, int dd) :d1(dd), B(666) { cout << "D1类的构造函数被调用" << endl; }
	void display() { B::display(); }
};
class D2 : virtual public B {
protected:
	int d2;
public:
	D2(int bb, int dd) :d2(dd), B(888) { cout << "D2类的构造函数被调用" << endl; }
	void display() { B::display(); }
};

class D3 :public D2, public D1 {
protected:
	int d3;
public:
	D3(int b, int d1, int d2, int d3) :d3(d3), B(666666), D1(b, d1), D2(b, d2) { cout << "D3类的构造函数被调用" << endl; }
	void display() { D1::display(); D2::display(); }
};
int main() {
	D3 obj(1, 2, 3, 4);
	obj.display();

	system("pause");
	return 0;
}

把B设置成虚基类后(即class D2 : virtual public B{...}; class D1 : virtual public B{...};)会发现,当创建D3类的对象obj时,只有D3类会通过构造函数调用基类的构造函数,而D1类和D2类的构造函数中不会调用基类的构造函数,即基类构造函数只能被调用1次(由最远的派生类调用)。
此时内存如下:
在这里插入图片描述
在这里插入图片描述

3.继续往下走:

在这里插入图片描述
当创建D4类的对象obj时,只有D4类会通过构造函数调用基类的构造函数,而D1类、D2类、D3类的构造函数中都不会调用基类的构造函数,即此时最远的派生类是D4。
代码如下:

#include<iostream>
using namespace std;
class B {
protected:
	int b;
public:
	B(int bb) :b(bb) { cout << "虚基类B的构造函数被调用" << endl; }
	void display() { cout << b << endl; }
};
class D1 :virtual public B {
protected:
	int d1;
public:
	D1(int bb, int dd) :d1(dd), B(666) { cout << "D1类的构造函数被调用" << endl; }
	void display() { B::display(); }
};
class D2 : virtual public B {
protected:
	int d2;
public:
	D2(int bb, int dd) :d2(dd), B(888) { cout << "D2类的构造函数被调用" << endl; }
	void display() { B::display(); }
};

class D3 :public D2, public D1 {
protected:
	int d3;
public:
	D3(int b, int d1, int d2, int d3) :d3(d3), B(666666), D1(b, d1), D2(b, d2) { cout << "D3类的构造函数被调用" << endl; }
	void display() { D1::display(); D2::display(); }
};
class D4 :public D3 {
protected:
	int d4;
public:
	D4(int b, int d1, int d2, int d3, int d4) :d4(d4), D3(b, d1, d2, d3), B(888888) { cout << "D4类的构造函数被调用" << endl; }
	void display() { D1::display(); D2::display(); D3::display(); }
};
int main() {
	D4 obj(1, 2, 3, 4, 5);
	obj.display();

	system("pause");
	return 0;
}

内存如下:
在这里插入图片描述
在这里插入图片描述
因此:除了虚基类的构造函数是无参或者不存在的情况下,在整个继承关系中,直接继承或间接继承虚基类的所有派生类,都必须在各自构造函数的初始化列表中列出对虚基类的初始化。

发布了146 篇原创文章 · 获赞 3 · 访问量 4987

猜你喜欢

转载自blog.csdn.net/ShenHang_/article/details/103660482