本文作者指出
"通常情况下,你不想访问太多其他类的内部,私有继承给了你一些额外的权力(和责任)。但私有继承并不是邪恶的;它只是维护成本更高,因为它增加了有人更改会破坏你代码的东西的可能性。"
假设Car
从Engine
私有继承的代码如下:
#include <iostream>
using namespace std;
class Engine
{
int numCylinders;
public:
class exception{};
Engine(int i) { if( i < 4 ) throw exception(); numCylinders = i; }
void start() { cout << "Engine started " << numCylinders << " cylinders" << endl; }
};
class Car : private Engine
{
public:
Car(int i) : Engine(i) {}
using Engine::start;
};
int main()
{
try
{
Car c(4);
c.start();
}
catch( Engine::exception& )
{
cout << "A Car cannot have less than 4 cylinders" << endl;
}
}
我的问题是:例如,Car
如何通过设置其具有少于4个圆柱体的Engine
,使用私有继承并且在基类中没有受保护的成员来破坏此代码?
我认为问题不在于Car可以破坏您的Engine代码,而在于通过更改Engine,有人可以破坏你的Car代码。继承表示一种比组合更紧密的耦合,因此Engine中的更改更有可能破坏从中继承的类,而不是包含它。在C++的情况下,通过让Car包含Engine指针或智能指针,可以实现更松散的耦合。
继承引入比成员关系更紧密耦合的一种方式是,派生类和基类的名称空间是混合的。因此,派生类上下文中名称的含义取决于基类引入的名称,并且存在常见的覆盖/隐藏效果。基类中的更改可能会对派生类中不一定可以清楚定位的代码产生影响,或者会立即产生有用的诊断。相比之下,如果成员对象的接口发生了变化,那么最容易破坏的将是实际提到该成员对象的代码。
我不认为Car可以设置Engine::numCylinders(至少没有访问原始内存之类的肮脏技巧)。文章中的示例使用了一个受保护的方法,即使用一个私有成员。
BTW:这篇文章从"尽可能使用合成"开始——汽车有发动机,但它不是发动机。当A从B派生时,这通常表示A是B。
文章的作者在前一点中提到了使用私有继承的一些"缺点",这里是:
- 如果你想每辆车包含几个发动机,就需要简单的成分变体
- 私有继承变体可能会引入不必要的多重继承
- 私有继承变体允许Car的成员将Car*转换为Engine*
- 私有继承变体允许访问基类的受保护成员
- 私有继承变体允许Car覆盖Engine的虚拟功能
- 私有继承变体使为Car提供一个start()方法稍微简单一些(20个字符,而不是28个字符),该方法只调用Engine的start()