继承 — 基础知识
继承对 C++ 重要吗?
有的。
一些倡导面向对象(OO)编程的人认为,继承是区分抽象数据类型(ADT)编程和面向对象编程的关键。
那些认为面向对象编程在哲学上不健全的人,仍然发现继承在以下方面很有用:在通过继承混合类来构造类时,利用空基类优化,这些混合类将数据或行为“捐赠”给结果类;或者在模板元编程中促进标签分发。
我何时会使用继承?
如果你遵循面向对象范式,将其用作规范化工具。
人类在两个维度上抽象事物:部分-和-种类-。一辆福特金牛座是汽车的一种,一辆福特金牛座有一个发动机、轮胎等等。“部分-的”层次结构自抽象数据类型(ADT)风格变得相关以来就一直是软件的一部分;继承增加了分解的“另一个”主要维度。
如果你不遵循面向对象范式,使用继承来从“捐赠”类中混入行为和数据,并帮助进行标签分发。
在 C++ 中如何表达继承?
通过 : public
语法
class Car : public Vehicle {
public:
// ...
};
我们用几种方式来表达上述关系:
Car
是Vehicle
的“一种”Car
“派生自”Vehicle
Car
是Vehicle
的“特化”Car
是Vehicle
的“子类”Car
是Vehicle
的“派生类”Vehicle
是Car
的“基类”Vehicle
是Car
的“超类”(这在 C++ 社区中不那么常见)
(注意:此 FAQ 涉及 public
继承;private
和 protected
继承是不同的。)
将派生类指针转换为其基类指针是否可以?
是的。
派生类的对象是基类的一种。因此,从派生类指针到基类指针的转换是完全安全的,并且一直都在发生。例如,如果我指向一辆汽车,我实际上是指向一辆交通工具,所以将 Car*
转换为 Vehicle*
是完全安全和正常的。
void f(Vehicle* v);
void g(Car* c) { f(c); } // Perfectly safe; no cast
(注意:此 FAQ 涉及 public
继承;private
和 protected
继承是不同的。)
public
、private
和 protected
有什么区别?
- 在类的
private
部分声明的成员(数据成员或成员函数)只能由该类的成员函数和友元访问。 - 在类的
protected
部分声明的成员(数据成员或成员函数)只能由该类的成员函数和友元访问,以及由派生类的成员函数和友元访问。 - 在类的
public
部分声明的成员(数据成员或成员函数)可以被任何人访问。
为什么我的派生类不能访问基类的 private
内容?
为了保护你免受基类未来变更的影响。
派生类无法访问基类的 private
成员。这有效地“隔离”了派生类,使其不受基类 private
成员任何更改的影响。
当我更改基类的内部部分时,如何保护派生类不被破坏?
一个类为两组不同的客户端提供了两个不同的接口:
- 它有一个服务于不相关类的
public
接口。 - 它有一个服务于派生类的
protected
接口。
除非您期望所有派生类都由您的团队构建,否则您应该将基类的数据成员声明为 private
,并使用 protected
inline
访问函数,派生类将通过这些函数访问基类中的 private
数据。这样,private
数据声明可以更改,但派生类的代码不会被破坏(除非您更改 protected
访问函数)。
有人告诉我永远不要使用受保护的数据,而应该总是使用私有数据和受保护的访问函数。这是一个好规则吗?
不。
每当有人对你说:“你应该_总是_把数据设为私有”,停下来——这是一个“总是”或“从不”的规则,我称这些规则为“一刀切”的规则。现实世界没有那么简单。
我是这样说的:如果我期望有派生类,我应该问这个问题:谁将创建它们?如果创建它们的人在你的团队之外,或者如果有_大量_的派生类,那么只有在这种情况下,才值得创建一个受保护的接口并使用私有数据。如果我期望派生类由我自己的团队创建,并且数量合理,那就不值得麻烦了:使用受保护的数据。并且抬起头来,不要感到羞愧:这是_正确的事情_!
受保护访问函数的好处是,当您更改数据时,您的派生类被破坏的频率不会像数据受保护时那样高。这样说吧:如果您认为您的用户在您的团队之外,那么您应该做的不仅仅是为您的私有数据提供获取/设置方法。您实际上应该创建另一个接口。您有一个面向一组用户的公共接口,以及一个面向另一组用户的受保护接口。但它们都需要一个精心设计的接口——为稳定性、可用性、性能等而设计。归根结底,将数据私有化(包括提供一个一致且尽可能不透明的接口)的真正好处是,当您更改数据结构时,避免破坏您的派生类。
但是,如果派生类是由您自己的团队创建的,并且数量合理地少,那么这根本不值得:使用受保护的数据。一些纯粹主义者(翻译:那些从未踏足现实世界的人,那些一生都生活在象牙塔里的人,那些不理解“客户”、“日程”、“截止日期”或“投资回报率”等词语的人)认为_一切_都应该可重用,_一切_都应该有一个干净、易用的接口。这类人很危险:他们经常使您的项目延迟,因为他们把一切都看得同样重要。他们基本上是在说:“我们有100项任务,我已经仔细地给它们排了优先级:它们都是优先级1。”他们让优先级的概念变得毫无意义。
你根本没有足够的时间让_每个人_的生活都轻松,所以你所能做的最好就是让世界上的一部分人的生活轻松。确定优先级。选择最重要的人,并花时间为他们构建稳定的接口。你可能不喜欢这样,但不是每个人都生而平等;有些人确实_比其他人更重要。我们对那些重要的人有一个称呼。我们称他们为“客户”。
好的,那么我到底该如何决定是否构建一个“protected
接口”?
三个关键词:投资回报率(ROI)、投资回报率(ROI)和投资回报率(ROI)。
你构建的每个接口都有成本和收益。你构建的每个可重用组件都有成本和收益。每个测试用例,每个结构清晰的东西,任何形式的投资。如果没有积极的投资回报,你_永远不应该_在_任何_事情上投入_任何_时间或_任何_金钱。如果它的成本高于它能为你公司节省的,那就不要做!
并不是每个人都同意我的观点;他们有犯错的权利。例如,那些与现实世界相距甚远的人表现得好像_每项_投资都是好的。毕竟,他们认为,如果你等得足够久,也许总有一天会为某人节省一些时间。也许吧。我们希望如此。
这种推理方式是不专业和不负责任的。你没有无限的时间,所以要明智地投资。当然,如果你住在象牙塔里,你不必担心那些烦人的“日程”或“客户”。但在现实世界中,你必须在日程内工作,因此你必须只在能获得良好回报的地方投入时间。
回到最初的问题:何时应该投入时间构建一个受保护的接口?答案:当您获得良好的投资回报时。如果它将花费您一小时,请确保它为某人节省了超过一小时,并且确保节省不是“海市蜃楼”。如果您能_在_当前项目_内_节省一小时,那简直是显而易见的:去做吧。如果它将在未来的某个项目上节省一小时,也许我们希望如此,那么就不要做。如果介于两者之间,您的答案将取决于您的公司如何权衡未来与现在。
重点很简单:不要做任何可能损害您日程的事情。(或者如果您做了,请确保您永远不要与我共事;我会让您付出代价。)投资是好的,_如果_该投资有回报。不要天真幼稚;成熟起来,认识到有些投资是糟糕的,因为它们总的来说成本高于回报。