当前位置:  开发笔记 > 编程语言 > 正文

我什么时候应该使用C++私有继承?

如何解决《我什么时候应该使用C++私有继承?》经验,为你挑选了5个好方法。

与受保护的继承不同,C++私有继承进入主流C++开发.但是,我仍然没有找到它的好用.

你什么时候使用它?



1> fizzer..:

我用它所有的时间.我头顶的几个例子:

当我想暴露一些但不是全部基类的接口时.公共继承将是一个谎言,因为Liskov可替代性被打破,而组合意味着编写一堆转发函数.

当我想从没有虚拟析构函数的具体类派生时.公共继承将邀请客户端通过指向base的指针进行删除,从而调用未定义的行为.

一个典型的例子是私下从STL容器派生:

class MyVector : private vector
{
public:
    // Using declarations expose the few functions my clients need 
    // without a load of forwarding functions. 
    using vector::push_back;
    // etc...  
};

实现适配器模式时,从Adapted类私下继承可以节省转发到封闭实例的过程.

实现私有接口.这经常出现在观察者模式中.通常我的Observer类,MyClass说,订阅自己的一些主题.然后,只有MyClass需要进行MyClass - > Observer转换.系统的其余部分不需要知道它,因此指示了私有继承.


不是那么懒惰(除非你的意思是好的).这允许创建在没有任何额外工作的情况下暴露的新函数重载.如果在C++ 1x中,他们为`push_back`添加了3个新的重载,`MyVector`免费获取它们.
@Julien__:是的,你可以写`template constexpr decltype(auto)f(Args && ... args)noexcept(noexcept(std :: declval ().f(std: :forward (args)...))和std :: is_nothrow_move_constructible ().f(std :: forward (args)...))> ){return m_base.f(std :: forward (args)...); 你可以用`Base :: f;`来编写.如果你想要私有继承和`using`语句给你的大部分功能和灵活性,你就拥有了每个函数的怪物(并且不要忘记`const`和`volatile`重载!).
@Krsna:实际上,我不这么认为.这里只有一个原因:懒惰,除了最后一个,这将是更难以解决.
我说大部分功能是因为你仍在调用一个在using语句版本中不存在的额外移动构造函数.一般来说,你会期望这个被优化掉,但理论上这个函数可以按值返回一个不可移动的类型.转发功能模板还具有额外的模板实例化和constexpr深度.这可能会导致您的程序遇到实施限制.

2> David Rodríg..:

回答接受后请注意:这不是一个完整的答案.如果您对这个问题感兴趣,请阅读此处(概念)和此处(理论和实践)等其他答案.这只是一个可以通过私有继承实现的花哨技巧.虽然它很花哨,但它不是问题的答案.

除了C++ FAQ中显示的私有继承的基本用法(在其他注释中链接),您可以使用私有和虚拟继承的组合来密封类(在.NET术语中)或使类最终(在Java术语中) .这不是常用的,但无论如何我发现它很有趣:

class ClassSealer {
private:
   friend class Sealed;
   ClassSealer() {}
};
class Sealed : private virtual ClassSealer
{ 
   // ...
};
class FailsToDerive : public Sealed
{
   // Cannot be instantiated
};

密封可以实例化.它派生自ClassSealer,可以直接调用私有构造函数,因为它是朋友.

FailsToDerive将无法编译,因为它必须调用ClassSealer直接构造(虚拟继承的要求),但它不能,因为它是在私人密封类,并在这种情况下FailsToDerive不是朋友ClassSealer.


编辑

评论中提到,当时使用CRTP不能使其成为通用的.C++ 11标准通过为befriend模板参数提供不同的语法来消除该限制:

template 
class Seal {
   friend T;          // not: friend class T!!!
   Seal() {}
};
class Sealed : private virtual Seal // ...

当然,这一切都没有实际意义,因为C++ 11 final为此目的提供了一个上下文关键字:

class Sealed final // ...


这可以是通用的,而不需要为每个要密封的类创建自定义ClassSealer!检查出来:class ClassSealer {protected:ClassSealer(){}}; 就这样.
+1.@Sasha:需要正确的虚拟继承,因为最派生的类总是直接调用所有虚拟继承类的构造函数,而普通继承不是这种情况.

3> Harper Shelb..:

私有继承的规范用法是"以"关系实现"(感谢Scott Meyers的'Effective C++'这个措辞).换句话说,继承类的外部接口与继承类没有(可见)关系,但它在内部使用它来实现其功能.


值得一提的是在这种情况下使用它的原因之一:这允许执行空基类优化,如果类是成员而不是基类,则不会发生这种情况.
使类不可复制也很方便 - 只需从一个不可复制的空类中私下继承.现在,您不必完成繁琐的声明工作,但不能定义私有复制构造函数和赋值运算符.迈耶斯也谈到了这一点.
它的主要用途是减少真正重要的空间消耗,例如在策略控制的字符串类或压缩对中.实际上,boost :: compressed_pa​​ir使用了protected inheritance.

4> Daemin..:

私有继承的一个有用用途是当你有一个实现接口的类,然后在其他对象中注册.您将该接口设置为私有,以便类本身必须注册,并且只有其注册的特定对象才能使用这些功能.

例如:

class FooInterface
{
public:
    virtual void DoSomething() = 0;
};

class FooUser
{
public:
    bool RegisterFooInterface(FooInterface* aInterface);
};

class FooImplementer : private FooInterface
{
public:
    explicit FooImplementer(FooUser& aUser)
    {
        aUser.RegisterFooInterface(this);
    }
private:
    virtual void DoSomething() { ... }
};

因此,FooUser类可以通过FooInterface接口调用FooImplementer的私有方法,而其他外部类则不能.这是处理定义为接口的特定回调的一种很好的模式.



5> Bill the Liz..:

我认为C++ FAQ Lite的关键部分是:

私有继承的合法,长期使用是当你想要构建一个使用类Wilma中的代码的Fred时,来自Wilma类的代码需要从你的新类Fred调用成员函数.在这种情况下,Fred在Wilma中调用非虚拟,而Wilma调用(通常是纯虚拟)本身,由Fred重写.这对组合来说要困难得多.

如果有疑问,你应该更喜欢组合而不是私人继承.

推荐阅读
勤奋的瞌睡猪_715
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有