何时使用 C++私有继承优于组合?

发布于 2024-11-14 11:33:58 字数 241 浏览 3 评论 0原文

你能给我一个具体的例子吗?什么时候使用私有继承优于组合?就我个人而言,我将使用组合而不是私有继承,但在某些情况下,使用私有继承可能是特定问题的最佳解决方案。阅读 C++ 常见问题解答,为您提供了使用的示例私有继承,但我似乎比私有继承更容易使用组合+策略模式甚至公共继承。

Can you give me a concrete example when is preferable to use private inheritance over composition? Personally, I will use composition over private inheritance, but there might be the case that using private inheritance is the best solution for a particular problem. Reading the C++ faq, gives you an example on using private inheritance, but I seems easier to use composition + strategy pattern or even public inheritance than private inheritance.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

陌生 2024-11-21 11:33:58

Scott Meyers 在“Effective C++”第 42 条中说

“只有继承才可以访问受保护的成员,并且只有继承才允许重新定义虚函数。因为存在虚函数和受保护的成员,所以私有继承有时是表达 is-implemented-in-terms 的唯一实用方法。之间的关系
类。”

Scott Meyers in "Effective C++" item 42 says

"Only inheritance gives access to protected members, and only inheritance allows for virtual functions to be redefined. Because virtual functions and protected members exist, private inheritance is sometimes the only practical way to express an is-implemented-in-terms-of relationship between
classes."

一生独一 2024-11-21 11:33:58

私有继承通常用于表示“implemented-in-terms-of”。我看到的主要用途是混合使用私有多重继承来构建具有来自各种混合父级的适当功能的子对象。这也可以通过组合来完成(我稍微喜欢),但是继承方法确实允许您使用 using 公开公开一些父方法,并且在使用 mixin 方法时允许使用稍微更方便的表示法。

private inheritance is typically used to represent "implemented-in-terms-of". The main use I have seen is for mixins using private multiple inheritance to build up a child object with the proper functionality from the various mixin parents. This can also be done with composition (which I slightly prefer) but the inheritance method DOES allow you to use using to expose some parent methods publicly, and allows for a slightly more convenient notation when using the mixin methods.

梅倚清风 2024-11-21 11:33:58

私有继承接口

许多人忽视的私有继承的典型应用如下。

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

Privately inheriting interfaces

A typical application of private inheritance that many people overlook is the following.

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;
};

Usually BigOne would be a class with a lot of responsibilities. In order to modularize your code you would break your code into components, that help doing the little stuff. These components shouldn't be friends of BigOne, but still they might need some access to your class, that you don't want to give into the public, because it's implementation details. Hence you create an interface for that component to provide this restricted access. This makes your code better maintainable and to reason about, because things have clear boundaries of access.

I used that technique a lot in a several man-year project and it has paid off. Composition is not an alternative here.

Letting the compiler generate a partial copy-constructor and assignment

Sometimes, there are copyable/movable classes that have a lot of different data members. The compiler generated copy or move constructor and assignment would be fine, except for one or two data members that need special treatment. This can be annoying, if data members are added, removed or changed frequently, since hand-written copy and move constructors and assignments need to be updated each time. It produces code-bloat and make the class harder to maintain.

The solution is to encapsulate the data members, whose copy and move operations can be compiler-generated into an extra struct or class from which you privately inherit.

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;
    }
};

You can then use the compiler-generated operations of the MyClassImpl class in the implementation of the respective operations of the class you are interested in. You could do the same with composition, but this would uglify your code in the rest of your class. If you used composition, the rest of the implementation would have to suffer because of this implementation detail of the copy and move operations. Private inheritance avoids this and avoids lots of code repetition.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文