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

news2024/9/24 11:24:47

📢博客主页: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;
};

👥总结

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

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

请添加图片描述

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

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

相关文章

超详细信息收集篇

1 域名信息收集 1.1 域名是什么 域名&#xff08;英语&#xff1a;Domain Name&#xff09;&#xff0c;又称网域&#xff0c;是由一串用点分隔的名字组成的 Internet 上某一台 计算机 或计算机组的名称&#xff0c;用于在数据传输时对计算机的定位标识&#xff08;有时也指地…

学习分布式事务遇到的小bug

一、介绍Seata 在处理分布式事务时我用到是Seata&#xff0c;Seata的事务管理中有三个重要的角色&#xff1a; TC (Transaction Coordinator) - 事务协调者&#xff1a;维护全局和分支事务的状态&#xff0c;协调全局事务提交或回滚。 TM (Transaction Manager) - 事务管理器…

DockerHub无法拉取镜像怎么办

快速构建企业级AIGC项目 LangChat是Java生态下企业级AIGC项目解决方案&#xff0c;在RBAC权限体系的基础上&#xff0c;集成AIGC大模型功能&#xff0c;帮助企业快速定制知识库、企业机器人。 网站文档&#xff1a;Index – LangChat 后台地址&#xff1a;LangChain Chat 前台…

3.5、matlab打开显示保存点云文件(.ply/.pcd)以及经典点云模型数据

1、点云数据简介 点云数据是三维空间中由大量二维点坐标组成的数据集合。每个点代表空间中的一个坐标点&#xff0c;可以包含有关该点的颜色、法向量、强度值等额外信息。点云数据可以通过激光扫描、结构光扫描、摄像机捕捉等方式获取&#xff0c;广泛应用于计算机视觉、机器人…

Redis之List列表

目录 一.列表讲解 二.列表命令 三.内部编码 四.应用场景 Redis的学习专栏&#xff1a;http://t.csdnimg.cn/a8cvV 一.列表讲解 列表类型是用来存储多个有序的字符串&#xff0c;如下所示&#xff0c;a、b、c、d、e五个元素从左到右组成了一个有序的列表&#xff0c;列表中的…

【连续四届EI检索|稳定ACM出版、EI检索|线上线下结合】2024年第五届医学人工智能国际学术会议(ISAIMS 2024,8月13-17)

第五届医学人工智能国际学术会议&#xff08;ISAIMS2024&#xff09;将于2024年8月13-17日于荷兰阿姆斯特丹自由大学召开&#xff0c;国内分会场将于2024年10月25-27日于中国武汉召开。 会议自2020年至今已经成功举办四届&#xff0c;吸引了来自海内外相关领域学者600余名。本届…

贪心算法(2024/7/16)

1合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xff1a;inter…

MongoDB教程(七):mongoDB分片

&#x1f49d;&#x1f49d;&#x1f49d;首先&#xff0c;欢迎各位来到我的博客&#xff0c;很高兴能够在这里和您见面&#xff01;希望您在这里不仅可以有所收获&#xff0c;同时也能感受到一份轻松欢乐的氛围&#xff0c;祝你生活愉快&#xff01; 文章目录 引言MongoDB 分…

Mysql数据库的概念及sql语法和规范+数据库的用户管理

一、数据库的概念 1.数据库&#xff1a;组织、管理、存储数据的仓库 2.数据库的管理系统&#xff08;DBMS&#xff09;&#xff1a;实现对数据有效组织&#xff0c;管理和存储的系统软件。 3.关系型数据库和非关系性数据库&#xff1a; 关系型数据库&#xff1a;mysql or…

神经网络概述

目录 1. 前馈神经网络(Feedforward Neural Networks, FNNs) 2. 卷积神经网络(Convolutional Neural Networks, CNNs) 3. 循环神经网络(Recurrent Neural Networks, RNNs) 4. 长短期记忆网络(Long Short-Term Memory, LSTM) 5. 门控循环单元(Gated Recurrent Unit…

MSPM0G3507——时钟主频拉到80MHZ

先点开使用时钟树 在配置时钟界面这样配置

【送书活动十期】从零开始node.js制作CLI工具

这篇博客的由来是源于工作中一个java项目的配置项是加密后的私钥&#xff0c;私钥是由其他项目中调用web3生成随机账号得到的&#xff0c;而加密方法只是简单在java项目中执行代码得到。这便导致两步操作有点割裂&#xff0c;需要有一个脚本来完成生成私钥和加密私钥&#xff0…

[Tensor学习]你不得不知道的知识点-切点-反转

wait...突然发现了一个错误&#xff1a; 在tensor里面只有size相同才允许相加&#xff0c; 如果想要相连接&#xff1a; PS: 如果tensor是多维的&#xff0c;比如说 a Tensor([1,2,3], [4,5,6]) 那么有 a[ : , :] a a[0,0] 1 a[ 第一维 &#xff0c;第二维] ...…

乘积量化pq:将高维向量压缩 97%

向量相似性搜索在处理大规模数据集时&#xff0c;往往面临着内存消耗的挑战。例如&#xff0c;即使是一个包含100万个密集向量的小数据集&#xff0c;其索引也可能需要数GB的内存。随着数据集规模的增长&#xff0c;尤其是高维数据&#xff0c;内存使用量会迅速增加&#xff0c…

MAC地址电脑的“身份证”

在电脑的世界里&#xff0c;每个设备都有一个独一无二的标识&#xff0c;就像我们的身份证号码一样。 这个标识被称为MAC地址&#xff08;Media Access Control Address&#xff09;&#xff0c;它是网络设备硬件的唯一识别码。 MAC地址由12个十六进制数字组成&#xff0c;通…

区块链资料

Quantstamp - Public Security Assessments smart-contract-sanctuary-bsc/contracts/mainnet at master tintinweb/smart-contract-sanctuary-bsc GitHub https://github.com/slowmist/Cryptocurrency-Security-Audit-Guide/blob/main/README_CN.md sFuzz: 高效自适应的智…

ArkUI组件——循环控制/List

循环控制 class Item{name: stringprice:number}private items:Array<Item> [new Item("A0",2399),new Item("BE",1999),new Item("Ro",2799)] ForEach(this.items,(item:Item) > {})List组件 列表List是一种复杂的容器&#xff0c;…

小程序里面使用vant ui中的vant-field组件,如何使得输入框自动获取焦点

//.wxml <van-fieldmodel:value"{{ userName }}"placeholder"请输入学号"focus"{{focusUserName}}"/>// .js this.setData({focusUserName: true});vant-field

postgresql删除用户

背景 **角色与用户**&#xff1a;在 PostgreSQL 中&#xff0c;用户和组的概念是通过“角色”来统一实现的。角色可以有登录权限&#xff08;在这种情况下&#xff0c;它们通常被称为“用户”&#xff09;&#xff0c;也可以没有&#xff08;在这种情况下&#xff0c;它们通常用…

网络请求之代理proxy

为什么要用代理呢&#xff1f;这个就涉及到安全问题了&#xff0c;你要是用一个IP频繁的访问人家网站&#xff0c;这不就是在搞事情么&#xff0c;人家网站肯定得把你的IP禁掉。所以我们爬虫的时候就得经常换IP&#xff0c;就是拿别人的IP不同人的IP访问网站去爬取数据。 我们…