解码构造与析构

news/2025/12/3 22:19:38/文章来源:https://www.cnblogs.com/YouEmbedded/p/19304165

构造与析构基础概念

核心定义

  • 构造函数:对象被创建时自动调用的特殊成员函数,唯一作用是初始化对象的成员属性,确保对象创建后处于合法可用状态。
  • 析构函数:对象被销毁前自动调用的特殊成员函数,用于释放对象占用的动态内存、文件句柄等资源,避免资源泄漏。
  • 编译器行为:若用户未自定义构造 / 析构函数,编译器会自动生成空实现的默认版本;并非强制要求用户提供,仅在需要自定义初始化 / 资源清理时才需手动定义。

生活类比

  • 构造函数:像新买手机的 “出厂激活 + 基础设置”,让手机从 “零件状态” 变成 “可使用状态”。
  • 析构函数:像旧手机报废前的 “数据删除 + 账号注销”,避免资源被滥用或泄露。

必要性

  • 未初始化的对象成员可能是随机值(野值),导致程序逻辑混乱、未定义行为;
  • 未清理的动态内存会造成内存泄漏,长期运行的程序会逐渐耗尽系统资源。

构造函数详解

基本语法与核心性质

class ClassName {
public:// 无参构造函数:没有参数ClassName(); // 有参构造函数:带参数,支持重载ClassName(int param1, string param2); // 拷贝构造函数:特殊的有参构造ClassName(const ClassName& obj); 
};
  • 性质
    • 函数名必须与类名完全一致,无返回值(连void都不能写);
    • 支持函数重载(参数个数 / 类型 / 顺序不同即可);
    • 对象创建时自动调用且仅调用一次
    • 若用户定义了任何构造函数(如带参构造),编译器不再生成默认无参构造函数

分类与调用方式

按参数分类

  • 无参构造:创建对象时不传参数ClassName obj;(注意:ClassName obj();是函数声明,不是对象创建!)
  • 有参构造:创建对象时传递参数初始化ClassName obj(10, "test");
  • 拷贝构造:用已存在的对象初始化新对象ClassName obj2 = obj1;

调用方式

  • 括号法:最常用,直观ClassName obj(10);(有参构造)、ClassName obj2(obj);(拷贝构造)
  • 显式法:直接调用构造函数生成临时对象ClassName obj = ClassName(10);(有参构造)、ClassName obj2 = ClassName(obj1);(拷贝构造)
  • 隐式转换法:仅适用于单参数构造函数ClassName obj = 10;(等价于ClassName obj(10);,编译器自动转换)

拷贝构造函数(重点)

核心作用

用一个已存在的对象,“复制” 出一个新对象,实现对象成员的完整初始化;本质是构造函数的重载形式,必须接收类对象的引用作为参数。

基本语法与参数要求

class ClassName {
public:// 拷贝构造函数/*** 拷贝构造函数:用已有对象初始化新对象* @param obj 被拷贝的源对象,const保证不修改源对象,引用避免值传递的无限递归*/ClassName(const ClassName& obj) {// 将obj的成员变量赋值给当前对象this->m_a = obj.m_a;this->m_b = obj.m_b;}
private:int m_a;string m_b;
};
  • 参数必须是const ClassName&的原因
    • const:防止函数内部意外修改源对象的属性,保证源对象安全;
    • 引用(&):若用值传递,参数传递时会触发拷贝构造函数的递归调用(值传递需要复制对象,复制又要调用拷贝构造,无限循环)。

调用时机

场景 1:用已存在对象初始化新对象

Cube c1(1,2,3); // 普通构造
Cube c2 = c1;   // 调用拷贝构造
Cube c3(c1);    // 等价于c2,调用拷贝构造

场景 2:值传递方式传递对象参数

/*** 测试函数:值传递接收对象* @param c 形参是Cube对象,值传递时会复制实参c1,触发拷贝构造*/
void testFunc(Cube c) {} Cube c1(1,2,3);
testFunc(c1); // 实参c1传递给形参c,调用拷贝构造

场景 3:以值方式返回局部对象

/*** 创建Cube对象并返回* @return Cube类型值,返回时会复制局部对象c,触发拷贝构造(编译器可能优化)*/
Cube createCube() {Cube c(1,2,3); // 局部对象return c;      // 返回值时创建临时对象,调用拷贝构造
}Cube c2 = createCube(); // 接收返回值,可能触发拷贝构造(编译器优化后可能省略)

深浅拷贝问题

浅拷贝(默认拷贝构造的行为)

image

编译器自动生成的拷贝构造函数,会执行逐成员的直接复制—— 如果成员是指针,仅复制指针的 “地址”,而非指针指向的内存内容。

class ShallowCube {
public:int* data; // 指针成员,指向堆内存/*** 普通构造函数:分配堆内存* @param d 初始化数据*/ShallowCube(int d) {data = new int(d); // 动态分配内存,存储d的值}/*** 析构函数:释放堆内存*/~ShallowCube() {delete data; // 释放data指向的内存}
};ShallowCube a(5); 
ShallowCube b = a; // 浅拷贝:b.data和a.data指向同一块堆内存
// 问题:a和b销毁时,析构函数会两次释放同一块内存,导致程序崩溃!

深拷贝(自定义拷贝构造解决浅拷贝问题)

image

手动为新对象分配独立的内存空间,并复制源对象指针指向的 “内容”,使新对象和源对象拥有独立的资源

class DeepCube {
public:int* data;/*** 普通构造函数:分配堆内存* @param d 初始化数据*/DeepCube(int d) {data = new int(d);}/*** 深拷贝构造函数:分配独立内存并复制内容* @param obj 源对象*/DeepCube(const DeepCube& obj) {data = new int(*obj.data); // 新分配内存,复制obj.data指向的值}/*** 析构函数:释放堆内存*/~DeepCube() {delete data;}
};DeepCube a(5);
DeepCube b = a; // 深拷贝:b.data指向新内存,与a.data独立
// 销毁时各自释放自己的内存,无冲突

编译器生成拷贝构造的规则

  • 默认生成条件:若用户未自定义拷贝构造函数,编译器自动生成浅拷贝版本

  • 停止生成条件

    • 若用户自定义了拷贝构造函数,编译器不再生成默认无参构造函数
    • 若用户自定义了任意构造函数(如带参构造),编译器仍会生成默认拷贝构造(浅拷贝);
  • 示例验证

    class Cube1 {
    public:Cube1(int a) {} // 用户定义带参构造
    };
    Cube1 c1(10);
    Cube1 c2 = c1; // 合法:编译器生成默认拷贝构造(浅拷贝)class Cube2 {
    public:Cube2(const Cube2& obj) {} // 用户定义拷贝构造
    };
    Cube2 c3; // 错误:编译器不生成默认无参构造,必须自定义无参构造
    

注意事项与最佳实践

  • 必须自定义拷贝构造的场景:类成员包含指针、动态分配内存(如new)、文件句柄、网络连接等 “独占资源” 时,必须实现深拷贝;

  • 禁用拷贝构造的场景:类管理的资源不允许复制(如单例类),可将拷贝构造声明为delete

    class NonCopyable {
    public:NonCopyable(const NonCopyable&) = delete; // 禁用拷贝构造
    };
    
  • 性能优化:传递大对象时,用const 类名&(常量引用)代替值传递,避免拷贝构造的开销:

    void func(const Cube& c); // 引用传递,不触发拷贝构造
    

析构函数

基本语法与核心性质

class ClassName {
public:/*** 析构函数:对象销毁时自动调用* 无参数、无返回值、不可重载*/~ClassName(); 
};
  • 性质
    • 函数名以~开头,与类名一致,无参数、无返回值;
    • 不可重载(一个类只能有一个析构函数);
    • 对象销毁时自动调用,负责清理资源(如delete动态内存、关闭文件)。

调用时机

  • 局部对象:离开作用域时(如函数执行完毕、代码块结束);
  • 动态对象:调用delete时(new创建的对象,需手动delete触发析构);
  • 全局对象:程序运行结束时;
  • 静态对象:所在作用域结束时(如函数内的static对象,函数执行完不销毁,程序结束时销毁);
  • 临时对象:表达式执行完毕时(如ClassName(10)生成的临时对象)。

初始化列表

语法与核心用途

class ClassName {
private:int m_var1;const int m_var2; // 常量成员string& m_var3;   // 引用成员
public:/*** 构造函数:用初始化列表初始化成员* @param v1 初始化m_var1* @param v2 初始化m_var2(常量必须初始化)* @param v3 初始化m_var3(引用必须初始化)*/ClassName(int v1, int v2, string& v3) : m_var1(v1), m_var2(v2), m_var3(v3) {}
};
  • 核心用途
    • 初始化常量成员const成员只能初始化,不能赋值);
    • 初始化引用成员(引用必须在声明时绑定对象,不能后期赋值);
    • 提升效率:直接初始化成员,避免 “先默认构造成员再赋值” 的额外开销;
    • 初始化基类成员(继承中常用)。

关键注意点

  • 成员初始化的顺序由声明顺序决定,与初始化列表的顺序无关:

    class Test {
    private:int a;int b;
    public:Test() : b(10), a(b) {} // 实际先初始化a(声明在前),a的值是随机的(b未初始化)
    };
    
  • 对于自定义类型成员(如string),初始化列表直接调用其构造函数,比函数体内赋值更高效。

类对象作为类成员(成员对象)

构造与析构顺序

  • 构造顺序:先调用成员对象的构造函数(按成员声明顺序),再调用外部类的构造函数;
  • 析构顺序:与构造顺序相反 —— 先调用外部类的析构函数,再调用成员对象的析构函数。

示例验证

class A {
public:A() { cout << "A的构造函数" << endl; }~A() { cout << "A的析构函数" << endl; }
};class B {
private:A a; // 成员对象(类A的对象)
public:B() { cout << "B的构造函数" << endl; }~B() { cout << "B的析构函数" << endl; }
};int main() {B b; // 创建B对象return 0;
}

输出结果:A 的构造函数B 的构造函数B 的析构函数A 的析构函数

说明

成员对象的构造函数参数,可通过外部类的初始化列表传递:

class A {
public:A(int x) { cout << "A的带参构造:" << x << endl; }
};class B {
private:A a; // 成员对象
public:// 通过初始化列表给A的构造函数传参B() : a(10) { cout << "B的构造函数" << endl; }
};

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/986090.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

敏捷冲刺日志 - Day 2

敏捷冲刺日志 - Day 2 站立会议 站立时会议改为线上进行。昨天已完成的工作:熟悉了项目初始版本(v1.0)的代码结构和功能。 分析了用户提出的三个核心需求:修复权限异常、界面汉化、增加批量处理。今天计划完成的工作…

10.结构型 - 代理模式 (Proxy Pattern)

代理模式 (Proxy Pattern) 在软件开发中,由于一些原因,客户端不想或不能直接访问一个对象,此时可以通过一个称为"代理"的第三者来实现间接访问.该方案对应的设计模式被称为代理模式. 代理模式(Proxy Design …

敏捷冲刺日志 - Day 1

敏捷冲刺日志 - Day 1 各个成员在 Alpha 阶段认领的任务 本次七天冲刺即为项目的 Alpha 阶段,目标是交付一个具备核心功能、可运行、可演示的最小可行产品(MVP)。团队成员在本阶段的任务分配如下:刘瑞康 (开发):负…

2025年中国集成灶十大品牌综合实力榜:选购指南与权威解析

body { font-family: "Microsoft YaHei", sans-serif; line-height: 1.8; color: rgba(51, 51, 51, 1); max-width: 1000px; margin: 0 auto; padding: 20px; background-color: rgba(249, 249, 249, 1) } h…

朝花夕拾OI回忆录

朝花夕拾 OI 回忆录 序言 或许是因为喜欢追忆吧,也或许是临近AFO,内心有一些触动,又或者是为了给后续的OIer一些前者的失败经验吧……总之,2025年12月3日,我十六岁生日这天,我决定写这篇 OI 回忆录,以记录我对O…

细胞因子:细胞信使的分子世界与功能解析

在复杂的多细胞生物体内,细胞间的信息交流是维持生命活动的基础。其中,细胞因子 作为一类关键的信使分子,在免疫调节、细胞生长、分化、炎症反应和组织修复等过程中扮演着不可或缺的角色。本文将深入探讨细胞因子的…

NOIp 的 p 是 painting 的 p!

哇还有连续剧。 作者在 CSP 后推完魔宴正在推 WA2。 Day -6 发现惊天理论:Day -3 最后的 ZR 有点娱乐赛,T1 是哈希表广告题,赛时裸 umap 拿了 90pts,赛后拿 umap 和 gp 卡了一万年卡不过,严肃学习 Dzb 牌哈希表,…

AWS云计算入门指南:从零到一,详解核心服务与免费套餐 - 教程

AWS云计算入门指南:从零到一,详解核心服务与免费套餐 - 教程2025-12-03 22:00 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !impor…

概率论直觉(二):方差与期望 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

图书馆管理系统项目冲刺 Day7

图书馆管理系统项目冲刺 Day7 一、站立式会议 照片: [插入Day6站立会议照片] 二、昨天已完成的工作 读者管理和个人中心完成 搜索优化和推荐系统实现 统计分析功能添加 三、今天计划完成的工作 何昊天: 实现系统设置…

就想赚点学分有什么不队-团队第三次作业—alpha冲刺

就想赚点学分有什么不队-团队第三次作业—alpha冲刺就想讨点学分有什么不队-冲刺总结 一、作业基本信息项目 内容这个作业属于哪个课程 https://edu.cnblogs.com/campus/fzu/202501SoftwareEngineering/这个作业要求在…

CSAPP 优化程序性能

目录优化程序性能程序剖析程序优化的基本原则编译器级别的优化指定优化等级函数内联优化编译器级别优化的局限性程序的性能程序性能的衡量对现代处理器的理解功能单元的性能代码级别的优化减少重复的运算和调用代码移动…

87键键盘的数字键对应快捷键含义

针对87键键盘的数字键对应的快捷键含义参考如下图:【参考】 VGN V98 Pro键盘使用说明作者:编程随笔 出处:http://www.cnblogs.com/nuccch/ 声明:本文版权归作者和博客园共有,欢迎转载,但请在文章页面明显位置给…

深入解析:Python异步(Asyncio)(一)

深入解析:Python异步(Asyncio)(一)pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Mo…

Java控制流程

Java流程控制 用户交互ScannerScanner工具类:获取用户的输入 基本语法:Scanner s = new Scanner(System.in);通过Scanner类的next()与nextLine()方法获取输入的字符串 在读取之前一般需要使用hasNext()与hasNextLine(…

快速判断是数字字符还是英文字符

问:(c >> 6 & 1) * 2 - 1是什么意思? 答:对于任意小写/大写英文字母字符,其 ASCII 码的二进制都形如 01xxxxxx;对于任意数字字符,其 ASCII 码的二进制都形如 0011xxxx。 根据这一特点,可以根据二进制…

2025最新成都旧房装修改造实力品牌推荐!家装标杆企业榜单发布,专业焕新服务助力品质生活升级

随着存量房时代到来,旧房装修改造成为家庭改善居住品质的核心需求。本榜单基于施工标准、环保工艺、空间规划、售后保障四大维度,结合成都市建筑装饰协会2024年度行业数据及业主满意度调研,权威解析2025年成都五大旧…

深入解析:神经流形:大脑功能几何基础的革命性视角

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

2025最新成都房屋装修公司推荐!成都家装市场权威榜单发布,品质服务双优助力打造理想家居

随着人们对居住品质要求的不断提升,房屋装修行业迎来新的发展机遇。本榜单基于设计实力、施工质量、材料环保性、服务体验四大维度,结合行业客户反馈及第三方评估数据,权威解析2025年成都五大房屋装修品牌综合实力,…

P6_MMSegmentation训练语义分割深度学习算法

P6_MMSegmentation训练语义分割深度学习算法6.1【G】训练语义分割模型 1.跑通代码 python tools/train.py Zihao-Configs/ZihaoDataset_FastSCNN_20230818.py 由于jupyter演示的代码模型太大,换了轻量级的FastSCNN_20…