【C++】拷贝构造函数及析构函数

📢博客主页:https://blog.csdn.net/2301_779549673
📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正!
📢本文由 JohnKi 原创,首发于 CSDN🙉
📢未来很长,值得我们全力奔赴更美好的生活✨

在这里插入图片描述

在这里插入图片描述

文章目录

  • 📢前言
  • 🏳️‍🌈什么是拷贝构造函数
    • ❤️1. 引用传参
    • 🧡2. 自动生成的拷贝构造函数
  • 🏳️‍🌈什么是析构函数
  • 👥总结


📢前言

当谈论C++编程语言的核心概念时,拷贝构造函数析构函数无疑是不可或缺的话题。它们不仅仅是理解对象生命周期和内存管理的关键,更是构建复杂系统和高效程序的基础。拷贝构造函数在对象复制过程中扮演着关键角色,决定了如何正确地复制对象的状态和数据。而析构函数则负责在对象生命周期结束时释放资源,确保程序运行的稳定性和性能。

在本博客系列中,我们将深入探讨C++中拷贝构造函数和析构函数的实现原理、使用场景以及最佳实践。我们将从基础知识入手,逐步扩展到高级应用和实际案例分析,帮助读者建立起对这两个重要概念的全面理解和应用能力。无论您是刚入门的初学者,还是希望深化专业知识的资深开发者,本系列都将为您提供有价值的内容和实用的技能,助力您在C++编程的道路上更进一步。让我们一起探索C++世界中的拷贝构造函数和析构函数,发现它们的力量和魅力!


🏳️‍🌈什么是拷贝构造函数

什么是拷贝构造函数
如果已经存在一个对象,我想对这个对象再复制一份,该怎么做呢?

有两种方法拷贝构造赋值运算符重载·,但显然赋值运算符重载不是这里的重点,这里要讲的是前者。至于后者笔者后续再补充。

拷贝构造函数是类的六大特殊成员函数之一,它是构造函数的一个重载形式

而且由于拷贝并不需要改变参数,所以参数部分还要用 “const”来修饰。

比如下面这样一个类

#include<iostream>
using namespace std;
class Date
{
public:
	
	Date(int year = 1, int month = 1, int day = 1)
	{
		_year = year;
		_month = month;
		_day = day;
	}

	void Print()
	{
		cout << _year << "/" << _month << "/" << _day << endl;
	}

private:
	int _year;
	int _month;
	int _day;
};

那么它的拷贝构造函数就可以写成这样

	//拷贝构造函数
	Date(const Date& d)
	{
		_year = d._year;
		_month = d._month;
		_day = d._day;
	}

❤️1. 引用传参

拷贝构造函数的参数只有一个且必须是类类型对象的引用,使用传值方式编译器直接报错,因为语法逻辑上会引发无穷递归调用。

因为传值传参是一种拷贝,每次拷贝时又需要先传值传参,就会导致无限递归,导致程序崩溃

在这里插入图片描述
顺便提一下传值返回的注意点

传值返回会产生一个临时对象调用拷贝构造,传值引用返回,返回的是返回对象的别名(引用),没有产生拷贝。这种方式避免了额外的拷贝开销,提升了性能。然而,如果返回的对象是函数内部的局部对象,函数结束时该对象的生命周期也随之结束,引用返回就会出现问题,因为引用将指向一个已经销毁的对象,类似于野指针的问题。

为了避免这种情况,需要注意以下几点:

  • 如果函数返回一个对象,且希望通过引用返回以避免拷贝开销,确保返回的对象是通过 new 创建的(即动态分配内存),这样它的生命周期不会受到函数作用域的限制。
  • 对于本地(即函数内部定义的)局部对象,不应该使用引用返回,因为这些对象在函数结束时会被销毁。在这种情况下,应该选择传值返回或者返回一个静态或全局对象,确保返回的对象在函数结束后仍然有效。

因此,在选择传值返回还是传引用返回时,必须根据返回对象的生命周期来进行合理的选择,以确保程序的正确性和性能的最优化。

🧡2. 自动生成的拷贝构造函数

在C++中,如果您没有显式定义拷贝构造函数,编译器将自动生成一个默认的拷贝构造函数。这个自动生成的拷贝构造函数执行的是浅拷贝,即简单地复制对象的每个成员变量的值,即一个一个字节地复制。这种行为对于大多数简单的类和结构体是合适的,可以有效地复制对象的状态。

自动生成的拷贝构造函数在以下情况下会被调用:

对象初始化:使用一个对象初始化另一个对象时,例如 ClassName obj1 = obj2;,编译器会调用自动生成的拷贝构造函数。

对象传递:将对象作为参数传递给函数,或从函数返回对象时,编译器也会使用拷贝构造函数来创建对象的副本。

尽管自动生成的拷贝构造函数对于许多情况都能正常工作,但在某些情况下可能需要显式地定义自定义的拷贝构造函数,特别是当类包含指针动态分配的资源时,以确保正确的深拷贝行为和资源管理。

就比如的创建往往是要动态内存开辟

typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}

	//拷贝函数
	Stack(const Stack& st)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		memcpy(_a, st._a, sizeof(STDataType) * st._top);
		_top = st._top;
		_capacity = st._capacity;
	}

	void Push(STDataType x)
	{
		if (_top == _capacity)
		{
			int newcapacity = _capacity * 2;
			STDataType* tmp = (STDataType*)realloc(_a ,sizeof(STDataType) * newcapacity);
			if (nullptr == tmp)
			{
				perror("malloc申请失败");
				return;
			}
			_a = tmp;
			_capacity = newcapacity;
		}
		_a[_top++] = x;
	}
	
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};

🏳️‍🌈什么是析构函数

析构函数(Destructor 是面向对象编程中的一个概念,它是一种特殊的成员函数,用于在对象生命周期结束时执行清理工作和资源释放操作。在许多编程语言中,包括C++和一些类似的语言,析构函数在对象销毁时自动被调用

在C++中,析构函数的名称与类名相同,但前面加上一个波浪号(~)。它没有参数,不返回任何值,也没有返回类型。析构函数的作用是在对象销毁时自动执行一些必要的清理步骤,如释放动态分配的内存、关闭文件、释放资源等。

比如说下面这就是一个基本的析构函数

#include <iostream>

class MyClass {
public:
    // 构造函数
    MyClass() {
        std::cout << "构造函数被调用" << std::endl;
    }

    // 析构函数
    ~MyClass() {
        std::cout << "析构函数被调用" << std::endl;
    }
};

int main() {
    MyClass obj; // 创建对象

    // 在main函数结束时,对象obj将销毁,析构函数会被自动调用
    return 0;
}

但是在C++中,如果您没有显式定义析构函数,编译器将自动生成一个默认的析构函数。这个自动生成的析构函数会依次销毁对象的每个成员变量,释放它们占用的内存空间。这对于大多数简单的类和结构体来说通常是合适的,因为它确保对象在生命周期结束时能够正确地释放资源。

自动生成的析构函数在以下情况下会被调用:

  • 对象离开作用域:当对象的作用域结束时,比如一个局部变量超出其作用域范围。
  • 动态分配的对象:如果对象是通过 new 运算符动态分配的,那么在调用 delete 释放内存时,也会自动调用析构函数。

尽管默认的析构函数对于大多数情况都能正常工作,但在涉及到动态分配的资源管理、对象组合等复杂情况下,可能需要显式定义自定义的析构函数。这样可以确保在对象销毁时执行额外的清理工作,如释放动态分配的内存或关闭文件等操作,以防止资源泄漏和程序错误。因此,了解和利用析构函数的自动生成特性对于C++程序的健壮性和性能优化至关重要。

就比如说下面这一块栈的析构函数,因为动态开辟了,所以需要手动释放空间,故要自定义析构函数

typedef int STDataType;
class Stack
{
public:
	Stack(int n = 4)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * n);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		_capacity = n;
		_top = 0;
	}

	//拷贝函数
	Stack(const Stack& st)
	{
		_a = (STDataType*)malloc(sizeof(STDataType) * st._capacity);
		if (nullptr == _a)
		{
			perror("malloc申请空间失败");
			return;
		}
		memcpy(_a, st._a, sizeof(STDataType) * st._top);
		_top = st._top;
		_capacity = st._capacity;
	}

	void Push(STDataType x)
	{
		if (_top == _capacity)
		{
			int newcapacity = _capacity * 2;
			STDataType* tmp = (STDataType*)realloc(_a ,sizeof(STDataType) * newcapacity);
			if (nullptr == tmp)
			{
				perror("malloc申请失败");
				return;
			}
			_a = tmp;
			_capacity = newcapacity;
		}
		_a[_top++] = x;
	}

	//析构函数
	~Stack()
	{
		//cout << "~Stack()" << endl;
		free(_a);
		_a = nullptr;
		_top = _capacity = 0;
	}
private:
	STDataType* _a;
	size_t _capacity;
	size_t _top;
};

👥总结

本篇博文对 拷贝构造函数及析构函数 做了一个较为详细的介绍,不知道对你有没有帮助呢

觉得博主写得还不错的三连支持下吧!会继续努力的~

请添加图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

百分点科技签约潍坊市数据产业发展战略合作

近日&#xff0c;潍坊市数据产业发展战略合作签约仪式举行&#xff0c;潍坊市人民政府副市长张震生&#xff0c;潍坊市财政局党组书记、局长王金祥&#xff0c;潍坊市大数据局党组书记陈强出席大会并致辞。百分点科技受邀进行战略合作签约&#xff0c;共同见证潍坊市数据要素市…

力扣—长度最小的子数组

文章目录 题目解析解题思路代码实现 题目解析 给定一个含有 n 个正整数的数组和一个正整数 target 。找出该数组中满足其总和大于等于 target 的长度最小的 子数组 [numsl, numsl1, …, numsr-1, numsr] &#xff0c;并返回其长度。如果不存在符合条件的子数组&#xff0c;返回…

捷配笔记-HDI板与普通PCB板的区别

HDI是什么? HDI(High Density Interconnect) 全称高密度互连板&#xff0c;是一种线分布密度高的高密度电路板&#xff0c;在现代电子设备中扮演着至关重要的角色。 它具有轻薄、线路密度高、有利于先进构装技术的使用、电气特性与信号更佳、改善射频干扰/电磁波干扰/静电释放…

matlab PID tuner整定工具箱的用法

从主页的APP中搜索到它&#xff1a; 按照下图IMPORT导入被控对象的传递函数 在下图的Inspect按钮中可以看到导入的被控对象的传函。 在下图的Type中选择控制器类型&#xff1a; 在下图的Form中选择PID的形式&#xff1a;有两种可选&#xff1a;平行式Parallel和标准式Standard …

K8S 上部署 Emqx

文章目录 安装方式一&#xff1a;快速部署安装方式二&#xff1a;定制化部署1. 使用 Pod 直接部署 EMQX Broker2. 使用 Deoloyment 部署 Pod3. 使用 Services 公开 EMQX Broker Pod 服务4. 通过 kubernetes 自动集群 EMQX MQTT 服务器5. 修改 EMQX Broker 的配置6. 赋予 Pod 访…

阿里云短信PHP集成api类

无需安装sdk扩展包&#xff0c;直接引入类即可使用 V3版本请求体&签名机制:自研请求体和签名机制 - 阿里云SDK - 阿里云 模版内容&#xff1a; <?phpnamespace common\components;use common\constant\UserConst; use common\models\bee\SmsReferer; use common\mode…

【VScode】安装【ESP-IDF】插件及相关工具链

一、ESP-IDF简介 二、VScode安装ESP-IDF插件 三、安装ESP-IDF、ESP-IDF-Tools以及相关工具链 四、测试例程&编译烧录 一、ESP-IDF简介 二、VScode安装ESP-IDF插件 【VScode】安装配置、插件及远程SSH连接 【VSCode】自定义配置 打开VScode&#xff0c;在插件管理搜索esp…

【程序大侠传】服务发布引发mq消息重复消费

前序 在编程武侠世界中&#xff0c;有一个门派“天机楼”&#xff0c;连接并协调各大门派之间的关系&#xff0c;确保整个江湖的运作流畅无阻。天机楼住要的业务范围主要如下&#xff1a; 信息传递的信使&#xff1a; 天机楼就像是江湖中的飞鸽传书&#xff0c;确保各门派之间…

泛微Ecology8明细表对主表赋值

文章目录 [toc]1.需求及效果1.1 需求1.2 效果2.思路与实现3.结语 1.需求及效果 1.1 需求 在明细表中的项目经理&#xff0c;可以将值赋值给主表中的项目经理来作为审批人员 1.2 效果 在申请人保存或者提交后将明细表中的人名赋值给主表中对应的值2.思路与实现 在通过js测…

【大型实战】企业网络实验(华为核心交换、ESXI7.0vmware虚拟机、DHCP中继、服务端网络及用户端网络配置)

需求 实验 vmware网络配置&#xff08;企业内部一般为ESXI&#xff09; 这样服务器虚拟机使用192.168.200.X网段才能与用户侧互通 vmware虚拟机配置&#xff08;DHCP服务器网络配置&#xff09; 打开网络管理页面 nmtui重置一下网络连接&#xff08;重启网卡&#xff09; …

JAVA @interface自定义注解(自定义注解+环绕通知 记录操作日志)

简介 注解interface是一种在Java代码中添加元数据&#xff08;metadata&#xff09;的方式&#xff0c;它可以用于提供程序的额外信息&#xff0c;但本身并不会直接影响程序的执行。注解可以应用于类、方法、字段和其他程序元素&#xff0c;用于提供关于这些元素的额外信息。 …

计算机组成原理 运算器

运算方法和运算器&#xff08;重点&#xff09; B二进制(binary), D十进制(decimal), H十六进制(hexadecimal) 纯小数和纯整数表示范围 设机器字长n1位&#xff0c;规定最高位&#xff08;第n1位&#xff09;为符号位 纯小数最大范围中的可理解为小数部分全为0的“1”&#…

TCP连接三次握手的过程,为什么是三次,可以是两次或者更多吗?

(1) 三次握手的过程 第一次握手&#xff1a;客户端向服务器发送一个包含SYN &#xff08;同步序列编号&#xff09;和初始序列号&#xff08;ISN&#xff09;的报文&#xff0c;请求建立连接&#xff0c;客户端进入SYN_SENT (同步已发送)状态。第二次握手&#xff1a;服务器收…

Python酷库之旅-第三方库Pandas(027)

目录 ​一、用法精讲 68、pandas.infer_freq函数 68-1、语法 68-2、参数 68-3、功能 68-4、返回值 68-5、说明 68-6、用法 68-6-1、数据准备 68-6-2、代码示例 68-6-3、结果输出 69、pandas.interval_range函数 69-1、语法 69-2、参数 69-3、功能 69-4、返回值…

Open3D Ransac拟合空间直线

目录 一、概述 1.1实现步骤 1.2优势与局限 二、代码实现 2.1关键代码 2.2完整代码 三、实现效果 前期试读&#xff0c;后续会将博客加入该专栏&#xff0c;欢迎订阅 Open3D点云算法与点云深度学习案例汇总&#xff08;长期更新&#xff09;-CSDN博客 一、概述 RANSAC&…

VScode终端和外部终端中文乱码问题

VScode终端和外部终端中文乱码问题 前言VScode终端VScode的第二大特点方法一方法二外部终端&#xff08;命令为ctrlf5&#xff09; 总结实现VScode终端和外部终端都能运行可执行文件 心得 前言 如果只想要看解决方案可直接跳转到总结部分&#xff0c;其余的章节只是用来说明原…

解决C#读取US7ASCII字符集oracle数据库的中文乱码

&#x1f468; 作者简介&#xff1a;大家好&#xff0c;我是Taro&#xff0c;全栈领域创作者 ✒️ 个人主页&#xff1a;唐璜Taro &#x1f680; 支持我&#xff1a;点赞&#x1f44d;&#x1f4dd; 评论 ⭐️收藏 文章目录 前言一、解决方法二、安装System.Data.OleDb连接库三…

第7章 模块(2)

目录 7.3 插入和删除模块 7.3.1 模块的表示 7.3.2 依赖关系和引用 7.3.3 模块的二进制结构 7.3.4 插入模块 7.3.5 移除模块 本专栏文章将有70篇左右&#xff0c;欢迎关注&#xff0c;查看后续文章。 7.3 插入和删除模块 两个系统调用&#xff1a; init_module&#xff1…

考研数学二战,怎么准备才能提升大?

一战70多...二战提升空间那是相当的大 我身边很多一战甚至不到60&#xff0c;二战成绩飙到120的&#xff0c;真的很猛 所以你根本不用担心是自己学数学没天赋&#xff0c;其实知识方法没用对而已 本人属于基础很差相当于是零基础的23考研党&#xff0c;经过一年备考成功上岸…

k8s集群 安装配置 Prometheus+grafana+alertmanager

k8s集群 安装配置 Prometheusgrafanaalertmanager k8s环境如下&#xff1a;机器规划&#xff1a; node-exporter组件安装和配置安装node-exporter通过node-exporter采集数据显示192.168.40.180主机cpu的使用情况显示192.168.40.180主机负载使用情况 Prometheus server安装和配置…