提问人:Gal Goldman 提问时间:12/17/2008 最后编辑:user366312Gal Goldman 更新时间:1/22/2022 访问量:44532
为什么我们实际上需要 C++ 中的私有继承或受保护的继承?
Why do we actually need Private or Protected inheritance in C++?
问:
在 C++ 中,我想不出我想从 基类:
class Base;
class Derived1 : private Base;
class Derived2 : protected Base;
它真的有用吗?
答:
Private 在很多情况下都很有用。其中只有一项是策略:
另一个有用的情况是禁止复制和分配:
struct noncopyable {
private:
noncopyable(noncopyable const&);
noncopyable & operator=(noncopyable const&);
};
class my_noncopyable_type : noncopyable {
// ...
};
因为我们不希望用户有一个指向我们对象的类型指针,所以我们是私下派生的。这不仅适用于不可复制的类,也适用于许多其他此类类(策略是最常见的)。noncopyable*
评论
当您希望访问基类的某些成员,但又不想在类接口中公开它们时,它非常有用。私有继承也可以看作是某种组合: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 类的封装,因为您可以访问其受保护的成员
- 你可以覆盖引擎虚拟方法,如果你的目标是一个简单的组合,这是你不想要的
评论
公共继承模型 IS-A。
非公共继承模型 IS-IMPLEMENTED-IN-TERMS-OF。
密闭模型 HAS-A,等同于 IS-IMPLEMENTED-IN-TERMS-OF。
萨特谈这个话题。他解释了何时选择非公共继承而不是包含来实现细节。
例如,当您想重用实现,而不是重用类的接口时,AND 会覆盖其虚函数。
我曾经在某个时候使用过私有继承和受保护的继承。
当您希望某些东西具有基类的行为,然后能够覆盖该功能,但您不希望整个世界都知道它并使用它时,私有继承非常有用。您仍然可以通过让函数返回私有派生类的接口来使用该接口。当您可以让事物自行注册以侦听回调时,它也很有用,因为它们可以使用私有接口注册自己。
当您有一个基类从另一个类派生有用的功能,但您只希望其派生类能够使用它时,受保护的继承特别有用。
我曾经将这些数据结构实现为类:
- 链表
- 泛型数组(抽象)
- 简单数组(继承自泛型数组)
- 大数组(继承自泛型数组)
大数组的接口会使它看起来像一个数组,但是,它实际上是固定大小的简单数组的链表。所以我是这样宣布的:
template <typename T>
class CBigArray : public IArray, private CLnkList {
// ...
评论
私人继承大多用于错误的原因。正如前面的答案中所示,人们将它用于 IS-IMPLEMENTED-IN-TERMS-OF,但根据我的经验,保留副本总是比从类继承更干净。另一个较早的答案,关于CBigArray的答案,为这种反模式提供了一个完美的例子。
我意识到在某些情况下,由于过度热心地使用“protected”,has-a 可能不起作用,但修复损坏的类比破坏新类要好。
评论
私有继承最有可能是一种合理的设计策略 当您处理两个与 IS-A 无关的类时,其中一个 要么需要访问另一个受保护成员,要么需要 重新定义其一个或多个虚拟功能。
摘自Scott Meyers,Effective C++ 3rd Edition 页面,191年。
下一个:强制某些运营商成为成员的理由
评论