为什么我们实际上需要 C++ 中的私有继承或受保护的继承?

Why do we actually need Private or Protected inheritance in C++?

提问人:Gal Goldman 提问时间:12/17/2008 最后编辑:user366312Gal Goldman 更新时间:1/22/2022 访问量:44532

问:

在 C++ 中,我想不出我想从 基类:

class Base;
class Derived1 : private Base;
class Derived2 : protected Base;

它真的有用吗?

C 继承 私有 保护 C++-FAQ

评论

4赞 spraff 1/4/2012
考虑一下:圆是一个椭圆,但圆不能代替椭圆,公共继承是一种 is-a 关系,尽管我们经常这样称呼它。
7赞 user4951 11/28/2013
圆确实是一个椭圆。不确定什么意思?

答:

42赞 Johannes Schaub - litb 12/17/2008 #1

Private 在很多情况下都很有用。其中只有一项是策略:

部分类模板专用化是这个设计问题的答案吗?

另一个有用的情况是禁止复制和分配:

struct noncopyable {
    private:
    noncopyable(noncopyable const&);
    noncopyable & operator=(noncopyable const&);
};

class my_noncopyable_type : noncopyable {
    // ...
};

因为我们不希望用户有一个指向我们对象的类型指针,所以我们是私下派生的。这不仅适用于不可复制的类,也适用于许多其他此类类(策略是最常见的)。noncopyable*

评论

1赞 Marcin 12/18/2008
不管你是公开派生还是私下派生不可复制的,因为复制构造函数和赋值运算符无论如何都是私有的。
7赞 Luc Touraille 12/18/2008
正如@litb在他的回答中所说,私下派生可以防止用户使用指针或对不可复制的引用来引用my_non_copyable_type的实例。
2赞 Johannes Schaub - litb 12/19/2008
是的,这还可以防止用户通过指向该不可复制项的指针进行删除。
0赞 Korben 3/1/2016
如果我们想阻止接口或成员探索到第三个调用者。我们是否应该简单地保护基类成员和函数?
60赞 Luc Touraille 12/17/2008 #2

当您希望访问基类的某些成员,但又不想在类接口中公开它们时,它非常有用。私有继承也可以看作是某种组合:C++ faq-lite 给出了以下示例来说明此语句

class Engine {
 public:
   Engine(int numCylinders);
   void start();                 // Starts this Engine
};

class Car {
  public:
    Car() : e_(8) { }             // Initializes this Car with 8 cylinders
    void start() { e_.start(); }  // Start this Car by starting its Engine
  private:
    Engine e_;                    // Car has-a Engine
};

为了获得相同的语义,您还可以按如下方式编写 car 类:

class Car : private Engine {    // Car has-a Engine
 public:
   Car() : Engine(8) { }         // Initializes this Car with 8 cylinders
   using Engine::start;          // Start this Car by starting its Engine
}; 

但是,这种做法有几个缺点:

  • 你的意图就不那么清楚了
  • 它可能导致滥用多重遗传
  • 它破坏了 Engine 类的封装,因为您可以访问其受保护的成员
  • 你可以覆盖引擎虚拟方法,如果你的目标是一个简单的组合,这是你不想要的

评论

24赞 Gal Goldman 12/18/2008
在我看来,在“有一个”的情况下,不应该有任何继承,但应该有一个组合。我认为这是滥用继承的一个坏例子,因为它使用户感到困惑。
9赞 Lodle 12/18/2008
第二个例子是一个非常糟糕的例子,否则你最终会像这样:类汽车:私人发动机,私人车轮,私人座椅
3赞 v.oddou 7/15/2015
是的,“has-a”不应该是继承。我将私有继承用于“is-implemented-in-temrs-of”。这实际上意味着“is-a”,但作为一种黑客。就像你重用了一段代码,它本来不应该产生这些后代,但是嘿......
16赞 Greg Rogers 12/17/2008 #3

公共继承模型 IS-A。
非公共继承模型 IS-IMPLEMENTED-IN-TERMS-OF。
密闭模型 HAS-A,等同于 IS-IMPLEMENTED-IN-TERMS-OF。

萨特谈这个话题。他解释了何时选择非公共继承而不是包含来实现细节。

3赞 Nemanja Trifunovic 12/18/2008 #4

例如,当您想重用实现,而不是重用类的接口时,AND 会覆盖其虚函数。

1赞 Dominik Grabiec 12/18/2008 #5

我曾经在某个时候使用过私有继承和受保护的继承。

当您希望某些东西具有基类的行为,然后能够覆盖该功能,但您不希望整个世界都知道它并使用它时,私有继承非常有用。您仍然可以通过让函数返回私有派生类的接口来使用该接口。当您可以让事物自行注册以侦听回调时,它也很有用,因为它们可以使用私有接口注册自己。

当您有一个基类从另一个类派生有用的功能,但您只希望其派生类能够使用它时,受保护的继承特别有用。

-1赞 isekaijin 12/18/2008 #6

我曾经将这些数据结构实现为类:

  • 链表
  • 泛型数组(抽象)
  • 简单数组(继承自泛型数组)
  • 大数组(继承自泛型数组)

大数组的接口会使它看起来像一个数组,但是,它实际上是固定大小的简单数组的链表。所以我是这样宣布的:

template <typename T>
class CBigArray : public IArray, private CLnkList {
    // ...

评论

4赞 12/18/2008
这是如何不使用私有继承的一个很好的例子。
3赞 user3458 12/18/2008 #7

私人继承大多用于错误的原因。正如前面的答案中所示,人们将它用于 IS-IMPLEMENTED-IN-TERMS-OF,但根据我的经验,保留副本总是比从类继承更干净。另一个较早的答案,关于CBigArray的答案,为这种反模式提供了一个完美的例子。

我意识到在某些情况下,由于过度热心地使用“protected”,has-a 可能不起作用,但修复损坏的类比破坏新类要好。

评论

0赞 curiousguy 8/18/2012
在许多情况下,这是一个可行的选择。但是,如果你最终得到了一个“可怕的死亡钻石”,那么编译器将无法帮助你(使用虚拟继承),你必须自己解决问题。
0赞 HenryTien 1/22/2022 #8

私有继承最有可能是一种合理的设计策略 当您处理两个与 IS-A 无关的类时,其中一个 要么需要访问另一个受保护成员,要么需要 重新定义其一个或多个虚拟功能。

摘自Scott Meyers,Effective C++ 3rd Edition 页面,191年。