虚基类与菱形继承
多重继承问题
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(); }
|
在这里,复印机继承自扫描仪和打印机,而打印机和扫描仪都继承自电器,形成了菱形继承。在实例化复印机时,调用了两次电器的构造函数,你可能只需要一份电器的副本,多余的调用形成了额外的开销,我们可以使用虚基类来避免这个问题。
使用虚继承的代码如下
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(); }
|
电器的构造函数只被调用了一次,达到了理想的效果。电器是在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";
}
|
猜测以上程序的输出?答案是:
类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字节。