什么时候使用C ++私有继承而不是组合?

时间:2011-06-09 18:13:10

标签: c++ inheritance composition strategy-pattern private-inheritance

你能给我一个具体的例子,当我更喜欢使用私人继承而不是作曲时?就个人而言,我将使用组合而非私有继承,但可能存在这样的情况:使用私有继承是特定问题的最佳解决方案。阅读C++ faq,给出了一个使用私有继承的示例,但我似乎比私有继承更容易使用组合+策略模式甚至公共继承。

3 个答案:

答案 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的朋友,但他们仍然可能需要访问您的课程,而您不想将其提供给公众,因为它的实施细节。因此,您为该组件创建一个接口以提供此受限访问。这使得您的代码更易于维护和推理,因为事物具有明确的访问边界。

我在几个人年项目中经常使用这种技术并且已经得到了回报。在这里,作品不是另类。

让编译器生成部分拷贝构造函数和赋值

有时候,有可复制/可移动的类有很多不同的数据成员。编译器生成的复制或移动构造函数和赋值都没问题,除了需要特殊处理的一个或两个数据成员。如果经常添加,删除或更改数据成员,这可能很烦人,因为每次都需要更新手写的复制和移动构造函数和赋值。它会产生代码膨胀并使类更难维护。

解决方案是封装数据成员,其复制和移动操作可以由编译器生成到您私下继承的额外structclass

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类的编译器生成的操作。您可以对组合执行相同操作,但这会使您的代码在你班上的其他人。如果您使用了合成,则由于复制和移动操作的此实现细节,实现的其余部分将不得不受到影响。私有继承避免了这种情况,并避免了大量的代码重复。

相关问题