你能给我一个具体的例子,什么时候最好使用私有继承而不是组合?就个人而言,我将使用组合而不是私有继承,但在某些情况下,使用私有继承可能是解决特定问题的最佳解决方案。阅读C++ 常见问题,给你一个使用私有继承的例子,但我似乎更容易使用组合+策略模式甚至公共继承而不是私有继承。
3 回答
Scott Meyers 在“Effective C++”第 42 条中说
“只有继承才能访问受保护的成员,只有继承才允许重新定义虚函数。因为存在虚函数和受保护的成员,所以私有继承有时是表达之间关系的唯一实用方法课。”
private
继承通常用于表示“在条款中实现”。我看到的主要用途是使用私有多重继承来构建具有来自各种 mixin 父级的适当功能的子对象的 mixin。这也可以通过组合来完成(我有点喜欢),但是继承方法允许您using
公开公开一些父方法,并且在使用 mixin 方法时允许使用更方便的表示法。
私有继承接口
许多人忽略的私有继承的典型应用如下。
class InterfaceForComponent
{
public:
virtual ~InterfaceForComponent() {}
virtual doSomething() = 0;
};
class Component
{
public:
Component( InterfaceForComponent * bigOne ) : bigOne(bigOne) {}
/* ... more functions ... */
private:
InterfaceForComponent * bigOne;
};
class BigOne : private InterfaceForComponent
{
public:
BigOne() : component(this) {}
/* ... more functions ... */
private:
// implementation of InterfaceForComponent
virtual doSomething();
Component component;
};
通常BigOne
是一个有很多职责的类。为了模块化你的代码,你可以将你的代码分解成组件,这有助于做一些小事情。这些组件不应该是 的朋友BigOne
,但它们仍然可能需要访问您的类,您不想将其公开,因为它是实现细节。因此,您为该组件创建一个接口以提供此受限访问。这使您的代码更易于维护和推理,因为事物具有明确的访问边界。
我在一个数人年的项目中大量使用了这种技术,并且得到了回报。组合在这里不是替代品。
让编译器生成部分复制构造函数和赋值
有时,有许多不同数据成员的可复制/可移动类。编译器生成的复制或移动构造函数和赋值会很好,除了需要特殊处理的一两个数据成员。如果频繁添加、删除或更改数据成员,这可能会很烦人,因为每次都需要更新手写的复制和移动构造函数和赋值。它会产生代码膨胀并使类更难维护。
解决方案是封装数据成员,其复制和移动操作可以编译器生成到额外的struct
或class
您私人继承的。
struct MyClassImpl
{
int i;
float f;
double d;
char c;
std::string s;
// lots of data members which can be copied/moved by the
// compiler-generated constructors and assignment operators.
};
class MyClass : private MyClassImpl
{
public:
MyClass( const MyClass & other ) : MyClassImpl( other )
{
initData()
}
MyClass( MyClass && other ) : MyClassImpl( std::move(other) )
{
initData()
}
// and so forth ...
private:
int * pi;
void initData()
{
pi = &p;
}
};
然后,您可以在您感兴趣的类的相应操作的实现中使用该类的编译器生成的操作MyClassImpl
。您可以对组合执行相同的操作,但这会使您的代码在类的其余部分变得丑陋。如果您使用组合,则由于复制和移动操作的此实现细节,其余的实现将不得不受到影响。私有继承避免了这种情况并避免了大量的代码重复。