你能给我一个具体的例子,当我更喜欢使用私人继承而不是作曲时?就个人而言,我将使用组合而非私有继承,但可能存在这样的情况:使用私有继承是特定问题的最佳解决方案。阅读C++ faq,给出了一个使用私有继承的示例,但我似乎比私有继承更容易使用组合+策略模式甚至公共继承。
答案 0 :(得分:13)
Scott Meyers在“Effective C ++”第42项中说
“只有继承才能访问受保护的成员,只有继承才允许重新定义虚函数。因为存在虚函数和受保护的成员,所以私有继承有时是表达is-implemented-in-terms的唯一实用方法。的关系 类“。
答案 1 :(得分:7)
private
继承通常用于表示“按条件实现”。我看到的主要用途是使用私有多重继承的mixin来构建具有各种mixin父项的适当功能的子对象。这也可以通过组合(我稍微偏好)完成,但继承方法允许您使用using
公开公开某些父方法,并且在使用mixin方法时允许使用稍微更方便的表示法。
答案 2 :(得分:4)
许多人忽略的私人继承的典型应用如下。
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
类的编译器生成的操作。您可以对组合执行相同操作,但这会使您的代码在你班上的其他人。如果您使用了合成,则由于复制和移动操作的此实现细节,实现的其余部分将不得不受到影响。私有继承避免了这种情况,并避免了大量的代码重复。