哈尔滨市网站建设_网站建设公司_Logo设计_seo优化
2026/1/13 9:57:23 网站建设 项目流程

《你真的了解C++吗》No.022:纯虚函数与抽象类的真正用途——契约式编程

导言:强制性的“蓝图”

在多态体系中,有时基类代表的是一个极其抽象的概念(比如“形状”或“文件对象”)。在这一层级,我们无法给出一个有意义的默认实现。

纯虚函数(Pure Virtual Function)允许我们只声明接口的“长相”,而将实现的责任完全推给派生类。这种机制将 C++ 类从单纯的“代码容器”提升到了“架构契约”的高度。


一、 语法本质:那一个= 0意味着什么?

当你写下virtual void draw() = 0;时,你并不是在给函数赋值为零,而是向编译器发出了三条指令:

  1. 禁止实例化:这个类现在成为了抽象类(Abstract Class)。你永远不能写出Shape s;这样的代码,编译器会报错。
  2. 虚表占位:在虚表(vtbl)中,该函数的条目被标记为一个特殊值(通常是空指针或指向一个名为__pure_virtual_called的错误处理函数)。
  3. 强迫继承者:任何继承自此类的子类,除非它也想当抽象类,否则必须重写(Override)这个函数。

二、 抽象类的核心用途:解耦与接口设计

1. 定义接口(Interface)

在 C++03 中,虽然没有interface关键字,但我们通过“只包含纯虚函数的类”来模拟它。这让调用者(客户端)只依赖于抽象的协议,而不依赖于具体的实现。

2. 实现契约式编程

抽象类定义了“做什么”,而派生类定义了“怎么做”。这种分离使得系统具备了极强的扩展性。例如,你可以编写一个处理Storage对象的函数,无论是DiskStorage还是CloudStorage,只要它们遵循Storage的契约,代码就能完美运行。


三、 纯虚函数可以有“身体”吗?

这是一个经常被忽略的冷知识:在 C++ 中,你可以为纯虚函数提供定义。

classShape{public:virtualvoiddraw()=0;};// 纯虚函数的定义voidShape::draw(){std::cout<<"Performing common shape prep...";}

为什么要这么做?
虽然子类仍然必须重写draw(),但子类的实现可以显式地调用Shape::draw()来执行一些通用的初始化逻辑。这既保证了子类必须提供个性化实现,又提供了共享代码的机会。


四、 避坑指南:纯虚析构函数

正如我们在 No.013 聊过的,基类的析构函数必须是虚的。但如果你想把类变成抽象类,却又没有合适的成员函数可以纯虚化,该怎么办?

你可以把析构函数声明为纯虚的:

classBase{public:virtual~Base()=0;};// 必须提供定义!Base::~Base(){}

注意:你必须为纯虚析构函数提供函数体。因为子类析构时会自动向上调用基类析构函数,如果没有定义,程序在链接阶段会崩溃。


总结:架构师的指挥棒

  • 虚函数:提供默认方案,你可以改,也可以不改。
  • 纯虚函数:不提供默认方案(通常),你必须自己动手。
  • 抽象类:它是思想的化身,不是物理存在的实体。

下一篇预告:我们通常谈论public继承表示“是一个(Is-a)”的关系。但如果你使用private继承,这种血缘关系就会变得非常诡异。它不再是“是一个”,而是“用……实现”。

➡️《你真的了解C++吗》No.023:私有继承的哲学——“实现基于”而非“是一种”。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询