一、拷贝构造函数的概念
拷贝构造函数用于创建一个与已有对象相同的对象,本质上也是构造函数的重载
拷贝构造函数只有一个类型为 const 类类型引用的形参,当我们要创建一个与已存在对象相同的对象时,由编译器自动调用拷贝构造函数。
class Date {
private:
int _year = 1;
int _month = 1;
int _day = 1;
public:
//打印
void Print()
{
cout << _year << " " << _month << " " << _day << endl;
}
//有参构造函数
Date(int year = 1, int month = 1, int day = 1)
{
_year = year;
_month = month;
_day = day;
}
//拷贝构造函数
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
}
};
int main()
{
Date d1(2024, 2, 6);
Date d2(d1);
d1.Print();
d2.Print();
return 0;
}
二、拷贝构造函数的参数必须是类类型对象的引用
拷贝构造函数的参数必须是类类型对象的引用,不能是传值。
//拷贝构造函数
Date(const Date& d)
{
_year = d._year;
_month = d._month;
_day = d._day;
_time = d._time;
}
如果是传值,Date d2(d1),将d1传给形参d,也会调用拷贝构造函数,然后再次传参,再次调用拷贝构造函数,无穷递归……而使用引用时,则会避免这个问题
//拷贝构造函数
Date(const Date d)
{
_year = d._year;
_month = d._month;
_day = d._day;
_time = d._time;
}
三、浅拷贝(值拷贝)
当我们未显式定义拷贝构造函数时,编译器会生成一个默认拷贝构造函数。该默认拷贝构造函数在进行对象拷贝时,对于内置类型会按照字节方式拷贝,对于自定义类型会调用自定义类型的拷贝构造函数。
其中,对于内置类型的拷贝按照字节方式拷贝,这种方式就是浅拷贝。
四、深拷贝
既然编译器默认生成的拷贝构造函数,已经可以按照字节方式进行对象的拷贝,那么我们为什么还要自己写拷贝构造函数呢?
因为编译器默认生成的拷贝构造函数只能进行按照字节方式拷贝的浅拷贝,对于某个指针变量来说,浅拷贝会将该指针变量所指向空间的地址拷贝给另一个对象。当这两个对象生命周期结束时,析构函数会释放同一块空间两次,导致程序崩溃。
因此,当类中有动态申请空间时,我们需要显式定义拷贝构造函数,即定义深拷贝。
五、拷贝构造函数的应用场景
1.使用已存在对象创建新的对象
Date d2(d1);
2.函数参数为类类型对象
void func(Date d);
3.函数返回值为类类型对象
Date func()
{
Date d(2024,2,16);
return d;
}
为了提高程序效率,通常在设计函数参数和返回值类型时,都设计为对象的引用(根据实际情况)