引言
在 C++ 面向对象编程中,对象的复制操作无处不在。无论是函数传参、返回值传递,还是对象间的赋值,都需要精确控制数据的复制行为。C++ 通过拷贝构造函数和赋值运算符重载两套机制,为开发者提供了对象复制的完整解决方案。本文将从基础概念出发,深入解析这两种复制机制的实现细节与应用技巧。
一、拷贝构造函数
如果一个构造函数的第一个参数是自身类型的引用,且其他所有参数都有默认值(如果有),就叫做拷贝构造,它是特殊的构造函数。
基本形式
#include <iostream>
using namespace std;
class Example {
public:
Example(const Example& d) {
// ...
}
};
1.1 解析:拷贝构造特点
这里有一些关键规则需要掌握:
- 拷贝构造函数是构造函数的一个重载。
- 拷贝构造函数的第一个参数必须是自身类类型的引用:
类名&或const 类名&(建议加const)。如果使用传值的方式,逻辑上会引发无穷递归调用。 - 拷贝构造函数可以有多个参数,第一个为引用,其他必须有缺省值。
- C++ 规定自定义类型对象进行拷贝行为必须调用拷贝构造,所以这里自定义类型传值传参和传值返回都会调用拷贝构造完成。
- 若未显式定义拷贝构造,编译器会生成自动生成拷贝构造函数。默认生成的拷贝构造对内置类型成员变量会完成值拷贝(浅拷贝),对自定义类型成员变量会调用它的拷贝构造。
- 类似
Date类,成员变量全是内置类型且不指向资源,编译器默认生成的拷贝构造就够了。类似Stack类,虽然也都是内置类型,但是指针指向资源,那么编译器默认生成的浅拷贝就不太够,需要显式定义深拷贝。再对于MyQueue类,自定义类型Stack变量成员就直接调用它的拷贝构造。
【技巧】:如果一个类显式实现了析构并释放资源,那么他就需要显式定义深拷贝,否则就不需要。
- 传值返回会产生一个临时对象来调用拷贝构造;而传引用返回,返回的是对象的别名,不会产生拷贝,但是返回的对象为一个当前函数局部域的局部对象,函数结束就会销毁,这时传引用返回是有问题的,类似于野指针。(传引用返回会减少拷贝,但是要确保返回对象在函数结束时不会被销毁)
解释特点第 2 条:
当拷贝构造函数传值传参时,函数的形参是实参拷贝出来的新对象,要调用拷贝构造,但是拷贝构造函数也是传值传参就又要调用拷贝构造,这样无限循环下去……
其次,在引用传参最好加上 const,因为将对象传过来,也不会将对象进行改变操作,那么 const 就方便了传参(权限缩小)。当然,这时候传 const 对象也是可以的(权限平移)。
特点第 2 条拓展:既然要引用传参,那么指针可以吗?
先说,传指针是可以的,但是函数就变成普通的构造函数,不是拷贝构造函数。
#include <iostream>
using namespace std;
{
:
( day = , month = , year = ) {
_day = day;
_month = month;
_year = year;
}
(Date* d) {
_day = d->_day;
_month = d->_month;
_year = d->_year;
}
{
cout << _year << << _month << << _day << endl;
}
:
_day;
_month;
_year;
};
{
Date d1;
d();
;
d();
;
}


