【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会指向同一个开辟的内存空间,这样就会导致析构指向的空间两次!

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

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

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

相关推荐
虾球xz27 分钟前
CppCon 2018 学习:EFFECTIVE REPLACEMENT OF DYNAMIC POLYMORPHISM WITH std::variant
开发语言·c++·学习
津津有味道2 小时前
Qt C++串口SerialPort通讯发送指令读写NFC M1卡
linux·c++·qt·串口通信·serial·m1·nfc
让我们一起加油好吗2 小时前
【C++】list 简介与模拟实现(详解)
开发语言·c++·visualstudio·stl·list
傅里叶的耶2 小时前
C++系列(二):告别低效循环!选择、循环、跳转原理与优化实战全解析
c++·visual studio
猫猫的小茶馆2 小时前
【STM32】预分频因子(Prescaler)和重装载值(Reload Value)
c语言·stm32·单片机·嵌入式硬件·mcu·51单片机
JeffersonZU2 小时前
Linux/Unix文件IO(文件描述符、原子操作、文件数据结构、open、read、write、fcntl、dup)
linux·c语言·unix·gnu
Vitta_U3 小时前
MFC的List Control自适应主界面大小
c++·list·mfc
Dovis(誓平步青云)3 小时前
基于探索C++特殊容器类型:容器适配器+底层实现原理
开发语言·c++·queue·适配器·stack
pipip.5 小时前
UDP————套接字socket
linux·网络·c++·网络协议·udp
智者知已应修善业6 小时前
【51单片机用数码管显示流水灯的种类是按钮控制数码管加一和流水灯】2022-6-14
c语言·经验分享·笔记·单片机·嵌入式硬件·51单片机