当前位置: 首页 > news >正文

Item34--区分接口继承和实现继承

这个 Item 讨论的是纯虚函数非纯虚函数(普通虚函数)和非虚函数在设计意图上的巨大差异。


1. 核心概念:三种函数的不同语义

public 继承体系下,基类的成员函数代表了三种不同的继承契约:

A. 纯虚函数 (Pure Virtual Functions)

  • 语法: virtual void draw() const = 0;
  • 含义: 只继承接口
  • 设计意图: “派生类必须提供这个功能,而且必须自己去实现它。”
  • 适用场景: 抽象的概念(如 Shape::draw()),基类无法提供合理的默认实现。

B. 非纯虚函数 (Impure / Simple Virtual Functions)

  • 语法: virtual void error(const std::string& msg);
  • 含义: 继承接口 + 继承一份默认实现
  • 设计意图: “派生类必须提供这个功能,如果你不想自己写,可以用我提供的默认版本。”
  • 适用场景: 大多数派生类的行为一致,但允许特例存在。

C. 非虚函数 (Non-Virtual Functions)

  • 语法: int objectID() const;
  • 含义: 继承接口 + 继承强制实现
  • 设计意图: “派生类必须继承这个功能,而且绝对不能修改它的行为(不变性)。”
  • 注意: 如果你在派生类中重新定义了非虚函数,就违反了 Item 36(绝不重新定义继承而来的 non-virtual 函数)。

2. “默认实现”的陷阱:飞机案例

Item 34 最精彩的部分在于指出了非纯虚函数(B类)带来的潜在风险。

场景:

假设你在设计一个航空公司系统。只有 A 型和 B 型飞机,它们的飞行方式完全一样。

class Airplane {
public:virtual void fly(const Airport& destination) {// 缺省行为:像普通客机一样飞行// ...飞往目的地的代码...}
};class ModelA : public Airplane { /* ... */ };
class ModelB : public Airplane { /* ... */ };

此时,ModelAModelB 都不需要写 fly 函数,直接继承基类的默认实现。这很方便,代码复用率高。

问题:

现在,公司引入了 C 型飞机(ModelC),但这并不是普通客机,而是一种滑翔机(它的飞行逻辑完全不同)。

如果你在写 ModelC 时,忘记了重写 fly 函数:

class ModelC : public Airplane {// 糟糕!程序员忘记声明 fly() 了
};

后果: 代码编译完全通过。但是当 ModelC 起飞时,它会试图调用 Airplane::fly 的默认逻辑(像喷气式客机一样飞),这可能会导致严重的逻辑错误(比如滑翔机没有引擎,却执行了引擎点火逻辑)。

根本原因: 普通虚函数提供了“接口”和“默认实现”,这不仅赋予了派生类使用默认实现的权利,也隐含了直接继承默认实现的风险


3. 安全的解决方案:分离接口与默认实现

为了避免上述悲剧,我们需要一种机制:提供默认实现,但迫使派生类必须显式地请求使用它,而不是如果不小心就自动继承了它。

Scott Meyers 提供了两种优雅的方案:

方案一:纯虚函数 + protected 辅助函数

fly 变成纯虚函数(强制派生类必须实现),然后将原本的默认逻辑放入一个独立的 protected 函数中。

class Airplane {
public:// 1. 纯虚函数:强制派生类必须自己声明 flyvirtual void fly(const Airport& destination) = 0; protected:// 2. 将默认逻辑剥离出来void defaultFly(const Airport& destination) {// ... 普通飞机的飞行代码 ...}
};class ModelA : public Airplane {
public:virtual void fly(const Airport& destination) override {defaultFly(destination); // 显式请求默认行为}
};class ModelC : public Airplane {
public:virtual void fly(const Airport& destination) override {// ... 实现滑翔机的独特飞行逻辑 ...}
};
  • 优点: 如果 ModelC 忘记写 fly,编译器会报错(因为基类 fly 是纯虚的)。
  • 缺点: 污染了命名空间(多了一个 defaultFly 函数)。

方案二:为纯虚函数提供定义(推荐)

这是一个很酷但鲜为人知的 C++ 特性:纯虚函数也可以有函数体

class Airplane {
public:virtual void fly(const Airport& destination) = 0;
};// 是的,纯虚函数可以有实现!
void Airplane::fly(const Airport& destination) {// ... 普通飞机的默认飞行代码 ...
}class ModelA : public Airplane {
public:virtual void fly(const Airport& destination) override {Airplane::fly(destination); // 显式调用基类的默认实现}
};class ModelC : public Airplane {
public:virtual void fly(const Airport& destination) override {// ... 实现滑翔机逻辑,不调用 Airplane::fly ...}
};
  • 机制: virtual ... = 0 迫使 ModelAModelC 必须重写 fly。但如果 ModelA 想要默认行为,它可以调用 Airplane::fly
  • 优点: 既强制了接口继承(必须重写),又提供了实现继承(可以复用),而且没有引入新的函数名。

4. 总结

在设计基类成员函数时,请根据你的意图选择正确的声明方式:

声明方式 继承了什么? 设计意图(潜台词)
纯虚函数 virtual void f() = 0; 仅接口 “你必须实现这个函数。我不提供默认版本(或者虽然提供了,但你必须显式调用)。”
非纯虚函数 virtual void f(); 接口 + 默认实现 “你应该实现这个函数。但如果你不写,可以用我提供的通用版本。”(小心:可能导致意外继承不该有的行为)
非虚函数 void f(); 接口 + 强制实现 “你必须继承这个函数,且不得修改它的行为。”
http://www.jsqmd.com/news/115976/

相关文章:

  • 【2025避坑指南】10款常见降AI率工具大汇总(含真实有效的免费降AI版本)
  • Item24--若所有参数皆需类型转换,请为此采用 non-member 函数
  • 基于java的SpringBoot/SSM+Vue+uniapp的赛车爱好者交流平台的详细设计和实现(源码+lw+部署文档+讲解等)
  • Item18--让接口容易被正确使用,不易被误用
  • 算法日记专题:位运算II( 只出现一次的数字I II III 面试题:消失的两个数字 比特位计数)
  • PyTorch - 指南
  • 【2025红黑榜】10款常见降AI率工具大汇总(含不限次数免费降AI版本)
  • Item25--考虑写出一个不抛异常的 swap 函数
  • Item25--考虑写出一个不抛异常的 swap 函数
  • 3562. 折扣价交易股票的最大利润
  • 【2025终极测评】10款常见降AI率工具大汇总(含0元免费降AI版本)
  • 算法日记专题:位运算I(汉明距离I II 面试题:判断是不是唯一的字符 丢失的数字 两个整数相加)
  • Item21--必须返回对象时,别妄想返回其 reference
  • Item15--在资源管理类中提供对原始资源的访问
  • 1985-2024年中国绿色专利数据库(绿色技术专利分类)
  • Item22--将成员变量声明为 private
  • Item16--`new` 与 `delete` 的对应规则
  • 3777. 使子字符串变交替的最少删除次数
  • item11--在 operator= 中处理“自我赋值
  • 预见2026:家居新品首秀平台选择战略——五大核心展会深度评估与推荐 - 匠子网络
  • Item20--宁以 pass-by-reference-to-const 替换 pass-by-value
  • 研究生必备!8个免费AI论文工具,半天生成5000字问卷论文还有高信度数据
  • Item20--宁以 pass-by-reference-to-const 替换 pass-by-value
  • Item17--以独立语句将 `new` 到的对象置入智能指针
  • Item17--以独立语句将 `new` 到的对象置入智能指针
  • 3433. 统计用户被提及情况
  • Item19--设计 class 犹如设计 type
  • 国外软件,安装即时专业版!
  • Item19--设计 class 犹如设计 type
  • basic_regex