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

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

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

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

相关推荐
yoyo勰11 分钟前
sqlite3
c语言·sqlite
No0d1es28 分钟前
GESP CCF C++六级编程等级考试认证真题 2024年12月
开发语言·c++·算法·青少年编程·gesp·ccf·六级
编码小哥37 分钟前
C++信号处理
c++
机器视觉知识推荐、就业指导42 分钟前
C++设计模式:组合模式(公司架构案例)
c++·后端·设计模式·组合模式
就爱学编程1 小时前
重生之我在异世界学编程之C语言:数据在内存中的存储篇(上)
c语言·数据结构
意疏1 小时前
【C 语言指针篇】指针的灵动舞步与内存的神秘疆域:于 C 编程世界中领略指针艺术的奇幻华章
c语言·开发语言·指针
长安05111 小时前
面试经典题目:LeetCode134_加油站
c++·算法·面试
越甲八千1 小时前
重拾设计模式--工厂模式(简单、工厂、抽象)
c++·设计模式
带多刺的玫瑰1 小时前
Leecode刷题C语言之考场就座
c语言·前端·javascript
LIZHUOLONG11 小时前
用C语言实现线程池
java·c语言·算法