news 2026/5/30 22:13:35

《你真的了解C++吗》No.029:抽象类的构造与析构——不存在的实体,存在的基石

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
《你真的了解C++吗》No.029:抽象类的构造与析构——不存在的实体,存在的基石

《你真的了解C++吗》No.029:抽象类的构造与析构——不存在的实体,存在的基石

导言:一个逻辑悖论?

在 No.022 中我们聊过,抽象类(包含纯虚函数的类)是无法实例化的。如果你写AbstractShape s;,编译器会直接把你挡在门外。

既然这种类永远不会在内存中独立存在,很多人就会产生疑问:它还需要构造函数和析构函数吗?答案是:不仅需要,而且它们在对象“拼图”的完成中起着不可替代的作用。


一、 物理本质:派生类是个“夹心饼干”

要理解为什么抽象类需要构造函数,必须看清派生类对象的内存物理排布。

当你创建一个Circle对象(继承自抽象类Shape)时,在内存中:

  1. 底层:是Shape的成员变量和虚指针。
  2. 上层:是Circle特有的成员变量。

构造规则
C++ 规定,任何对象的初始化必须遵循**“先祖后辈”**的顺序。当Circle构造时,它必须首先调用Shape的构造函数来初始化基类那部分的成员。即使Shape是抽象的,它依然拥有需要被初始化的数据(比如colorvptr)。


二、 抽象类构造函数的“幕后黑手”

抽象类的构造函数通常被声明为protected。这样做有两个原因:

  1. 限制访问:既然外界不能实例化它,暴露public构造函数没有意义。
  2. 强制协作:它明确告诉开发者,这个构造函数是专门给子类在初始化列表(Member Initialization List)里调用的。
classShape{// 抽象类protected:intid;Shape(inti):id(i){}// 依然可以有逻辑public:virtualvoiddraw()=0;};classCircle:publicShape{public:Circle(inti):Shape(i){}// 子类必须显式调用基类构造};

三、 析构函数:抽象类最容易犯错的地方

这是本章最核心的警示:抽象类的析构函数绝不能省略,且必须是虚的(virtual)。

即使一个类是抽象的,当通过基类指针删除子类对象时,如果析构函数不是虚的,就会发生内存泄漏:

Shape*p=newCircle();deletep;// 如果 ~Shape() 不是虚的,Circle 的析构函数永远不会被调用

纯虚析构函数的特殊性
你甚至可以把析构函数声明为纯虚的,来强迫这个类变成抽象类:
virtual ~Shape() = 0;
但请记住,你必须为这个纯虚析构函数提供函数体Shape::~Shape() {}),因为子类析构时,最终一定会向上调用基类的析构函数。如果没有定义,链接器会报错。


四、 编译器生成的“隐形代码”

即使你没在抽象类里写构造函数,编译器也会为你生成一个默认的。
这个隐形函数最重要的任务不是初始化变量,而是初始化虚指针(vptr)

  • Shape的构造过程中,vptr会被暂时指向Shape的虚表。
  • 随后在Circle的构造过程中,vptr才会被重写为指向Circle的虚表。
    这个“vptr 演变”的过程,决定了我们在下一章(No.030)要讨论的那个致命陷阱。

总结:抽象类不“空”

  • 构造函数:是为了确保派生类中属于基类的那部分内存被正确初始化。
  • 析构函数:是为了确保通过多态删除对象时,清理链条不会断裂。
  • 抽象类是一个半成品,构造函数则是组装这个半成品不可或缺的工序。
版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/28 18:19:41

MALG模块优化和修复的bug梳理(二)

Bug #1: 内存泄漏修复 文件: mlag/src/mlag_pkt.c /***************************************************************************** * 函数名 : mlagUpdateDadNexthop * 负责人 : * 创建日期:20180731 * 函数功能:更新DAD下一跳信息 * 输入参数:无 * 输出参数:无 …

作者头像 李华
网站建设 2026/5/29 1:12:02

3.10 DaemonSet和Job控制器:节点守护进程与批处理任务完整实战

3.10 DaemonSet和Job控制器:节点守护进程与批处理任务完整实战 引言 DaemonSet和Job是Kubernetes中用于特殊场景的控制器。DaemonSet确保每个节点运行一个Pod副本,常用于日志收集、监控等场景。Job用于运行一次性任务或批处理任务。本文将详细介绍这两个控制器的使用方法。…

作者头像 李华
网站建设 2026/5/28 21:45:17

开源可二开的二手车小程序源码系统 带完整的搭建部署教程

温馨提示:文末有资源获取方式 在数字化营销时代,许多二手车商家选择使用标准化SaaS平台,却常常受限于功能固化、数据无法自主、定制成本高昂等问题。真正的商业竞争力,往往源于拥有一个能够随业务成长而灵活进化、数据完全自主的专…

作者头像 李华
网站建设 2026/5/30 12:45:19

工时统计报表怎么做才有用?对比2026年7款工具的报表与分析能力

本文将深入对比2026年7款项目工时管理工具:PingCode、Worktile、Jira、Wrike、Replicon、Harvest、Clockify。 一、项目工时管理工具怎么选:把“填工时”变成“管项目” 很多团队把工时管理理解成“加一张填报表”。但真正能产生价值的工时管理&#xff…

作者头像 李华
网站建设 2026/5/28 12:19:24

软件开发项目管理系统大盘点:9款工具对比,哪个最适合你的团队?

本文将深入对比9款面向软件开发项目的项目管理系统:PingCode、Jira Software、Azure DevOps、GitLab、GitHub、YouTrack、Rally、TAPD、CODING DevOps。一、为什么通用项目管理工具不够用很多团队在早期会用通用项目管理工具来管研发:建几个看板列、把任…

作者头像 李华
网站建设 2026/5/28 18:56:26

语音交互界面(VUI)自动化测试技术实施方案

一、方案背景与目标 ‌行业痛点‌ 语音识别准确率受环境噪声、方言等因素干扰显著多轮对话场景的状态管理复杂度高,易出现上下文丢失传统UI测试工具无法有效捕捉语音交互的时序与语义特征 ‌核心目标‌ 构建端到端自动化测试覆盖率 ≥85%将语音指令识别验证耗时…

作者头像 李华