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

C++—拷贝构造函数

一、拷贝构造函数

1.定义

是特殊的构造函数,用一个已存在的对象来初始化另一个新对象。只拷贝数据成员,函数成员是共有的。

class ClassName {
public:// 拷贝构造函数ClassName(const ClassName& other) {// 成员变量的拷贝操作}
};

注意点:

(1)参数类型为const 类名&

①const的使用是为了防止修改被拷贝的对象

②&(引用)的使用是防止无限递归调用,如果把一个真实的类对象作为参数传递到拷贝构造函数,会无穷递归。

(2)当没有自定义拷贝构造函数时,编译器会自动生成一个默认的版本。默认拷贝构造函数会采用浅拷贝的方式,即逐个复制对象的成员变量。如果需要管理动态资源,需要手动处理深拷贝。

2.构造函数和拷贝构造函数的区别

①构造函数的参数列表可以是任意参数,而拷贝构造函数的参数是同类对象的引用

②构造函数在创建对象时调用,拷贝构造函数在用已有对象初始化新对象时调用

3.拷贝构造函数的使用场景

(1)用于拿一个对象初始化另一个对象

(2)函数的形参是类的对象

当对象作为值参数传递给函数时,会创建实参的副本,此时调用拷贝构造函数。

(3)函数的返回值是类对象

当函数返回对象时,会创建返回值的临时副本(亡值对象),此时调用拷贝构造函数。

亡值对象只存在函数调用中,函数结束亡值销毁。

注意:

①如果函数结束,但对象生存周期还未到,则可以使用引用或者指针返回。

②引用返回的场景:函数结束但变量仍然可以存在。

4.拷贝构造函数和赋值语句的重载

(1)拷贝构造函数的重载

用于创建一个新的对象,并用传入对象的值进行初始化。

(2)赋值语句的重载

用于将一个对象的值赋给另一个对象。

(3)赋值语句的重载与拷贝构造函数的区别

赋值语句重载需要处理自赋值的情况,并返回对象的引用以支持链式赋值

二、对象的生存期

1.动态创建对象

使用new操作符在堆上创建对象,并使用delete操作符释放对   

注意:new 和 delete 必须配对。

①使用 new 分配的内存必须用 delete 释放。

②使用 new[] 分配的内存必须用 delete[] 释放。

2.局部对象

局部对象的生存期由函数调用控制。从定义处开始,到作用域结束时自动销毁。

3.全局对象的生存期

全局对象的生存期由程序运行控制。程序启动时创建,程序结束时销毁。

三、内存泄漏和delete操作符的使用

1.内存泄漏

程序在动态分配内存后未正确释放,导致系统内存逐渐减少。

2.delete操作符的使用

用于释放动态分配的内存空间。

3.delete操作符的工作原理

调用对象的析构函数,并调用free释放堆区内存空间。

4.注意事项

delete操作符后应将指针置为空,避免悬挂指针。

四、内存分配的协议

1.new操作符的协议

当使用 new[] 分配一个包含析构函数的对象数组时,C++ 运行时通常会在实际对象数据前多分配 4 或 8 字节(取决于平台),用于存储以下信息:

(1)数组长度:记录数组中对象的数量,以便 delete[] 知道需要调用多少次析构函数。

(2)额外元数据:可能包括内存块大小、分配标志等。

注意:

仅对需要析构的对象有效,如果类没有自定义析构函数(且成员也不需要析构),编译器可能优化掉头部。

2.delete操作符的协议

当调用 delete[] 时,运行时会

(1)读取头部数据:获取数组长度。

(2)调用析构函数:按顺序为每个对象调用析构函数(次数 = 头部记录的长度)。

(3)释放内存:使用free将整个内存块(包括头部)归还给系统。

如图所示:(在visualStudio平台下,多分配4个字节)

3.new和malloc的区别

特性new/deletemalloc/free
构造 / 析构函数自动调用构造函数和析构函数不调用任何构造 / 析构函数
内存分配协议可能多分配头部存储元数据(如数组长度)仅分配请求的大小,无额外头部
类型安全返回正确的指针类型返回 void*,需要手动转换类型
异常处理分配失败时抛出 std::bad_alloc分配失败时返回 NULL
适用场景面向对象编程(创建对象)面向过程编程(分配原始内存)

总结:

new[] 的协议:为需要析构的对象数组多分配头部,存储数组长度。

delete[] 的协议:读取头部长度,调用对应次数的析构函数,释放整块内存。

与 malloc 的本质区别new 是面向对象的内存分配,自动管理对象生命周期;

              malloc 是低级内存分配,只处理原始字节。

五、常量对象和非常量对象

1.const与类型结合

表示该类型的变量是一个常变量

如图所示:

2.常量对象的定义

使用const关键字修饰对象,初始化后不可修改其状态,但可以被引用。
注意:必须在定义时初始化,且之后不能被赋值。

3.区别

特性常量对象非常量对象
状态修改禁止修改允许修改
可调用函数仅常量成员函数所有成员函数
mutable 成员可修改可修改
典型场景只读数据、多线程共享对象需动态修改的对象

ps:在C++中,合理的使用const可以提高代码的安全性和可读性!!!

http://www.xdnf.cn/news/15134.html

相关文章:

  • 深度学习模型表征提取全解析
  • SpringBoot mybatis
  • LeetCode经典题解:1、两数之和(Two Sum)
  • [2025CVPR]Mr. DETR:检测Transformer的多路由指导训练解析
  • SpringGateway网关增加https证书验证
  • Redis-哨兵选取主节点流程
  • 第4章:实战项目一 打造你的第一个AI知识库问答机器人 (RAG)
  • 数据库技术体系及场景选型方案
  • 【机器学习】BeamSearch算法
  • 10.9 大模型训练数据优化实战:3步让准确率从68%飙升至79%
  • 3、Unity免费插件整合
  • git入门之安装tortoisegit
  • UNet改进(19):基于残差注意力模块Residual Attention的高效分割网络设计
  • 三码合一:OneCode注解驱动的新时代编码范式
  • 【算法训练营Day10】栈与队列part2
  • TCP协议格式与连接释放
  • Python 机器学习核心入门与实战进阶 Day 7 - 复盘 + 综合实战挑战
  • java idea 本地debug linux服务
  • C++STL-String
  • 题解:P13017 [GESP202506 七级] 线图
  • spring-data-jpa + Alibaba Druid多数据源案例
  • Flutter基础(前端教程⑧-数据模型)
  • NHibernate案例
  • thinkphp使用redis抢单实例
  • 提取出Wallpaper Engine壁纸的mpkg类静态壁纸
  • LKT4304稳定可靠高兼容性国产安全加密芯片
  • Linux操作系统之进程间通信:管道概念
  • Qt6中模态与非模态对话框区别
  • 基于Spring Boot+Vue的巴彦淖尔旅游网站(AI问答、腾讯地图API、WebSocket及时通讯、支付宝沙盒支付)
  • 以太网基础④IP 协议介绍与 IP 校验和算法实现