【C++】深入剖析默认成员函数3:拷贝构造函数

上两篇博客我们深入探讨了一下另外两个默认成员函数:构造函数和析构函数。

他们两个都有一个特性:(1)对"内置类型"的成员函数:不进行初始化,也不进行清理(就是没有调用析构函数)。

(2)对于"自定义类型"的成员函数:会调用它对应的默认构造函数进行初始化,和对应的析构函数进行清理。

而今天我们迎来了第三问默认成员函数---拷贝构造函数!

听到"构造"两个字,想必大家马上就意识到这个宝贝构造函数跟构造函数有着微妙的关系吧?是的!

拷贝构造函数的概念:

  • 如果⼀个构造函数的第⼀个参数是⾃⾝类类型的引⽤,且任何额外的参数都有默认值,则此构造函数也叫做拷⻉构造函数,也就是说拷⻉构造是⼀个特殊的构造函数。(一般用const修饰)

当然,作为一个特殊的默认成员函数,同样也符合一下构造函数的特性:

  • 拷贝构造函数是构造函数的⼀个重载。
  • 拷贝构造函数的第⼀个参数必须是类类型对象的引用,使用传值方式编译器直接报错(否则就会无限递归调用)
  • 拷贝构造函数也可以多个参数,但是第⼀个参数必须是类类型对象的引用,后⾯的参数必须有缺省值。

代码实现如下:

cpp 复制代码
class Date
	 {
public:
	 Date(int year = 1, int month = 1, int day = 1)
		 {
		 _year = year;
		 _month = month;
		 _day = day;
		 }
	
		 // 编译报错:error C2652: "Date": ⾮法的复制构造函数: 第⼀个参数不应是"Date"
		 //Date(Date d)
		 Date(const Date & d)
	{
	     _year = d._year;
	     _month = d._month;
	     _day = d._day;
	}
		Date(Date* d)
	{
		_year = d->_year;
		_month = d->_month;
		_day = d->_day;
	}
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main() {
	Date d1(2024, 12, 22);
	//另外一种写法:d1(d2)
	Date d2 = d1;

	d1.Print();
	d2.Print();
	return 0;
}

运行结果:

运行结果就成功地将d1的值拷贝给d2啦!

当然,这里肯定还有一个问题:

为啥一定要使用引用传参呢?传值传参会怎么样吗?

那就让我们来验证一下:

运行结果:

简单来说,就是没有使用引用传参的话,就会形成无限递归传参,类似于套娃

而且,如果不需要改变参数,建议把const加上!

代码如下:

运行结果:

这才是正确的用法,否则就会引起编译器报错,奔溃.....

记住,只要你不需要改变函数体内容,就必须把const加上(别问为什么,因为跟我说一定要加上的人也没有说为什么)

默认的拷贝构造函数:

这里的默认拷贝构造函数就跟前面的构造函数和析构函数有所不同了

  • C++规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里自定义类型传值传参和传值返回都会调用拷贝构造完成。
  • 若未显式定义拷贝构造,编译器会自动生成拷贝构造函数。⾃动⽣成的拷贝构造对内置类型成 员变量会完成值拷贝/浅拷贝(⼀个字节⼀个字节的拷贝),对⾃定义类型成员变量会调用他的拷贝构造。

代码实现如下:

cpp 复制代码
class Date
	 {
public:
	 Date(int year = 1, int month = 1, int day = 1)
		 {
		 _year = year;
		 _month = month;
		 _day = day;
		 }
	
		 // 编译报错:error C2652: "Date": ⾮法的复制构造函数: 第⼀个参数不应是"Date"
		 //Date(Date d)
	//	 Date(const Date& d)//使用引用传参
	//{
	//     _year = d._year;
	//     _month = d._month;
	//     _day = d._day;
	//}
		/*Date(Date* d)
	{
		_year = d->_year;
		_month = d->_month;
		_day = d->_day;
	}*/
	void Print()
	{
		cout << _year << "-" << _month << "-" << _day << endl;
	}
private:
	int _year;
	int _month;
	int _day;
};

int main() {
	Date d1(2024, 12, 22);
	//另外一种写法:d1(d2)
	Date d2 = d1;

	d1.Print();
	d2.Print();
	return 0;
}

运行结果:

相比于构造函数和析构函数,拷贝构造函数对内置函数和自定义函数都会进行处理

对于默认的拷贝构造函数,我们也不是完全就使用默认的,比如Stack类的,我们就需要自己实现拷贝,否则如果使用Stack st1拷贝出的Stack st2会指向同一个开辟的内存空间,这样就会导致析构指向的空间两次!

这样也就会导致程序报错,奔溃.....

以上就是对于拷贝构造函数的深入剖析

有问题希望佬们及时指出哦~

相关推荐
明月看潮生2 小时前
青少年编程与数学 02-018 C++数据结构与算法 06课题、树
数据结构·c++·算法·青少年编程·编程与数学
小指纹2 小时前
动态规划(一)【背包】
c++·算法·动态规划
zhaoyqcsdn2 小时前
抽象工厂模式及其在自动驾驶中的应用举例(c++代码实现)
c++·经验分享·笔记·设计模式
天若有情6733 小时前
用 C++ 模拟 Axios 的 then 方法处理异步网络请求
网络·c++·php
h39744 小时前
MFC文件-写MP4
c++·windows·音视频·mfc
阿让啊4 小时前
单片机获取真实时间的实现方法
c语言·开发语言·arm开发·stm32·单片机·嵌入式硬件
FightingLod4 小时前
STM32版I²C相亲指南(软件硬件双修版)
c语言·stm32·单片机
姝孟5 小时前
学习笔记(C++篇)--- Day 4
c++·笔记·学习
Leon_az6 小时前
c++内存池
c++
三体世界7 小时前
Linux 管道理解
linux·c语言·开发语言·c++·git·vscode·visual studio