虚基类与菱形继承

多重继承问题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <iostream>
class PoweredDevice
{
public:
PoweredDevice() {
std::cout << "PoweredDevice created" << "\n";
}
};
class Scanner: public PoweredDevice
{
public:
Scanner() {
std::cout << "Scanner created" << "\n";
}
};
class Printer: public PoweredDevice
{
public:
Printer() {
std::cout << "Printer created" << "\n";
}
};
class Copier: public Scanner, public Printer
{
public:
Copier() {
std::cout << "Copier created" << "\n";
}
};
int main() {
Copier* cp = new Copier();
}

在这里,复印机继承自扫描仪和打印机,而打印机和扫描仪都继承自电器,形成了菱形继承。在实例化复印机时,调用了两次电器的构造函数,你可能只需要一份电器的副本,多余的调用形成了额外的开销,我们可以使用虚基类来避免这个问题。

image-20240410124447039

使用虚继承的代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>

class PoweredDevice
{
public:
PoweredDevice() {
std::cout << "PoweredDevice created" << "\n";
}
};

class Scanner: virtual public PoweredDevice
{
public:
Scanner() {
std::cout << "Scanner created" << "\n";
}
};

class Printer: virtual public PoweredDevice
{
public:
Printer() {
std::cout << "Printer created" << "\n";
}
};

class Copier: public Scanner, public Printer
{
public:
Copier() {
std::cout << "Copier created" << "\n";
}
};

int main() {
Copier* cp = new Copier();
}

image-20240410125022030

电器的构造函数只被调用了一次,达到了理想的效果。电器是在Scanner还是在Printer创建时被构造呢?实际上,Copier可以直接调用非直接父类的构造函数一次即可。如果一个类继承了一个或者多个具有虚父类的类,则最后被派生的类会负责构造虚基类。

虚基类大小

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>

class A {
public:
int a;
};
class B :virtual public A {
public:
int b;
};
class C :virtual public A {
public:
int c;
};
class D :public B, public C {
public:
int d;
};
int main() {
std::cout << sizeof(A) << "\n"
<< sizeof(B) << "\n"
<< sizeof(C) << "\n"
<< sizeof(D) << "\n";

}

猜测以上程序的输出?答案是:

image-20240410125527264

类A:只有一个int,所以为4字节。

类B:所有继承虚基类的类都将有一个虚表,所以类的实例会存储一个虚表指针大小为8字节(64位),同时还有a,b两个整形,总共16字节。

类C:与类B相同。

类D:因为C++存在内存对齐机制,所以在D中的B和C实例都是long+int,于是对齐到16字节,A的实例为4字节,加上D本身的int整形,所以共32+8=40字节。