拷贝构造函数的概念
1. 只有单个形参,该形参是对本类类型对象的引用(一般常用 const 修饰),
在用已存在的类类型对象创建新对象时由编译器自动调用。
2.C++ 会为类生成一个默认拷贝构造函数和赋值操作符。这两个默认实现的行为都是逐位复制(即浅拷贝),它只是简单地复制每个成员的值,包括指针的地址,而不分配新的内存空间。
拷贝构造函数的特性
它也是一个特殊的成员函数,所以他符合构造函数的一些特性:
① 拷贝构造函数是构造函数的一个重载形式。函数名和类名相同,没有返回值。
② 拷贝构造函数的参数只有一个,并且 必须要使用引用传参!
深拷贝 构造函数来替换掉程序中的 浅拷贝构造函数。
拷贝构造函数启动规则
类名 对象1
启动规则如下:
类名 对象2(对象1)
类名 对象2 = 对象1
类名 * 对象2 = new 类名(对象1)
格式
类名(const 类名 & other)
需要编写拷贝构造函数的原因
使用 编译器提供的
拷贝构造函数 以及 赋值运算符 会将指针指向的空间也赋值过去
深拷贝 自己写
浅拷贝 使用 编译器提供的
浅拷贝
浅拷贝只是简单地将对象的值(包括指针的地址)赋值给新的对象。对于指针成员,浅拷贝只是复制指针本身,而不会为指针指向的动态内存分配新的空间。这意味着两个对象中的指针将指向同一块内存。
浅拷贝的风险
如果两个对象都指向同一块内存,当其中一个对象修改这块内存时,另一个对象也会受到影响。此外,当对象被销毁时,如果两个对象都尝试释放同一块内存,就会导致 双重释放(double deletion)错误,程序可能崩溃
#include <iostream>
class Date {
public:
Date(int year = 0, int month = 1, int day = 1) {
_year = year;
_month = month;
_day = day;
}
/* Date d2(d1); */
Date(Date& d) { // 这里要用引用,否则就会无穷递归下去
_year = d._year;
_month = d._month;
_day = d._day;
}
void Print() {
printf("%d-%d-%d\n", _year, _month, _day);
}
private:
int _year;
int _month;
int _day;
};
int main(void)
{
Date d1(2022, 3, 9);
Date d2(d1); // 拷贝复制
// 看看拷贝成功没
d1.Print();
d2.Print();
return 0;
}
深拷贝、
是为指针成员分配新的内存空间,并复制原对象中的数据到新的内存。这保证了每个对象都有自己独立的一份数据,不会共享同一块内存,因此也不会引发双重释放的问题。
#include <iostream>
#include <cstring>
class Date {
public:
Date(int year = 0, int month = 1, int day = 1, const char* desc = "") {
_year = year;
_month = month;
_day = day;
_desc = new char[strlen(desc) + 1]; // 分配内存
strcpy(_desc, desc); // 复制描述字符串
}
// 深拷贝构造函数
Date(const Date& d) {
_year = d._year;
_month = d._month;
_day = d._day;
_desc = new char[strlen(d._desc) + 1]; // 分配内存
strcpy(_desc, d._desc); // 复制描述字符串
}
// 析构函数,释放动态内存
~Date() {
delete[] _desc; // 释放内存
}
void Print() const {
printf("%d-%d-%d: %s\n", _year, _month, _day, _desc);
}
private:
int _year;
int _month;
int _day;
char* _desc; // 日期描述字符串
};
int main(void)
{
Date d1(2022, 3, 9, "Important date");
Date d2(d1); // 深拷贝构造
// 查看两个对象是否完全独立
d1.Print();
d2.Print();
return 0;
}
注意
深拷贝和浅拷贝的区别
浅拷贝:
如果类中没有显性的定义拷贝构造函数,编译器会提供一个默认的拷贝构造函数,这个默认的拷贝构造函数,只完成成员之间的简单赋值。如果类中没有指针成员,只用这个默认的拷贝构造函数,是没有问题的。
深拷贝:
如果类中有指针成员,并且使用浅拷贝,指针成员之间也是只做了简单的赋值。相当于两个对象的指针
在C++中,当你定义了一个空类时,编译器会默认生成哪些函数?请写出它们的声明格式。
class Test {}; // 定义了一个空类Test
// 编译器默认生成的函数如下:
// 1. 默认构造函数:
Test(void);
// 2. 拷贝构造函数:
Test(const Test &other);
// 3. 析构函数:
~Test(void);
// 4. 拷贝赋值运算符:
const Test &operator=(const Test &other);