在课程“多重继承”中我们曾以“菱形继承问题”作结。本节将继续深入讨论这一话题。
注意:本节属于进阶内容,可按需跳过或略读。
一、菱形继承问题
下列示例(补充了构造函数)展示了菱形继承:
#include <iostream>
class PoweredDevice
{
public:
PoweredDevice(int power)
{
std::cout << "PoweredDevice: " << power << '\n';
}
};
class Scanner : public PoweredDevice
{
public:
Scanner(int scanner, int power)
: PoweredDevice{ power }
{
std::cout << "Scanner: " << scanner << '\n';
}
};
class Printer : public PoweredDevice
{
public:
Printer(int printer, int power)
: PoweredDevice{ power }
{
std::cout << "Printer: " << printer << '\n';
}
};
class Copier : public Scanner, public Printer
{
public:
Copier(int scanner, int printer, int power)
: Scanner{ scanner, power }, Printer{ printer, power }
{
}
};
你或许期望继承图如下:
然而,默认情况下,创建 Copier
对象会得到 两份 PoweredDevice
子对象——分别来自 Scanner
与 Printer
:
简短示例验证:
int main()
{
Copier copier{ 1, 2, 3 };
return 0;
}
输出:
PoweredDevice: 3
Scanner: 1
PoweredDevice: 3
Printer: 2
可见 PoweredDevice
被构造两次。
有时这正是所需行为;另一些场合则希望 Scanner
与 Printer
共享 一份 PoweredDevice
。
二、虚基类
要使基类共享,只需在继承列表中加入 virtual
关键字,形成所谓 虚基类。此时继承树中只有一份基类对象,且仅构造一次。下面给出简化示例:
class PoweredDevice { };
class Scanner : virtual public PoweredDevice { };
class Printer : virtual public PoweredDevice { };
class Copier : public Scanner, public Printer { };
现在创建 Copier
对象,每个 Copier
仅含一份共享的 PoweredDevice
。
但新问题随之产生:若 Scanner
、Printer
共享 PoweredDevice
,谁来负责构造?答案是 最派生类 Copier
。Copier
构造函数可直接调用非直接基类 PoweredDevice
的构造函数:
#include <iostream>
class PoweredDevice
{
public:
PoweredDevice(int power)
{
std::cout << "PoweredDevice: " << power << '\n';
}
};
class Scanner : virtual public PoweredDevice // PoweredDevice 现为虚基类
{
public:
Scanner(int scanner, int power)
: PoweredDevice{ power } // 创建 Scanner 对象时需写此语句,但实例化 Copier 时会被忽略
{
std::cout << "Scanner: " << scanner << '\n';
}
};
class Printer : virtual public PoweredDevice // PoweredDevice 现为虚基类
{
public:
Printer(int printer, int power)
: PoweredDevice{ power } // 创建 Printer 对象时需写此语句,但实例化 Copier 时会被忽略
{
std::cout << "Printer: " << printer << '\n';
}
};
class Copier : public Scanner, public Printer
{
public:
Copier(int scanner, int printer, int power)
: PoweredDevice{ power }, // 由 Copier 构造 PoweredDevice
Scanner{ scanner, power }, Printer{ printer, power }
{
}
};
再次运行:
int main()
{
Copier copier{ 1, 2, 3 };
return 0;
}
输出:
PoweredDevice: 3
Scanner: 1
Printer: 2
PoweredDevice
仅构造一次。