c++——浅拷贝和深拷贝、浅赋值和深赋值
目录
一、拷贝构造函数
1.定义
2.按位拷贝
(1)定义
(2)特点
(3)使用场景
二、浅拷贝
1.什么是浅拷贝
2.特点
三、深拷贝(适用于对态资源)
1.什么是深拷贝
2.特点
四、拷贝赋值运算符的深赋值和浅赋值
1.浅赋值
2.深复制
五、总结
一、拷贝构造函数
1.定义
用已有对象来初始化新对象。如果用户没有自定义拷贝构造函数,则系统默认提供一个缺省的拷贝构造函数。
强调:默认的拷贝构造函数是按位拷贝的方式初始化。
2.按位拷贝
(1)定义
是一种对象复制方式,指将一个对象的所有二进制位(bit)原样复制到另一个对象,即逐字节(byte)复制对象在内存中的原始数据。
强调:按位拷贝是编译器默认的拷贝行为,包含默认拷贝构造函数和默认拷贝赋值运算符的实现逻辑。
(2)特点
①不执行任何自定义代码,仅复制内存数据。
②对指针类型,仅复制指针值(地址),不复制指向的内存。
③当对象包含动态资源(比如堆内存)时,会导致多个对象共享同一块内存,调用析构函数后会导致重复释放问题。此时,不合适使用按位拷贝。
(3)使用场景
①对象成员都是基本类型
class Point {
private:int x; // 基本类型,无动态资源int y;
public:// 编译器生成的默认拷贝构造函数会执行按位拷贝Point(int x_, int y_) : x(x_), y(y_) {}
};int main() {Point p1(10, 20);Point p2 = p1; // 按位拷贝:复制x=10和y=20,安全有效return 0;
}
②不需要手动释放资源
③对象的指针成员指向全局内存或字符串常量
class ReadOnlyData {
private:const char* str; // 指向字符串常量(全局资源)
public:ReadOnlyData(const char* s) : str(s) {}
};int main() {ReadOnlyData d1("hello");ReadOnlyData d2 = d1; // 按位拷贝:d2.str和d1.str指向同一常量,安全return 0;
}
注意:字符串常量存放在数据区,无需手动释放。
二、浅拷贝
根据下面的代码找到运行时出现的问题
class MyString
{
private:char* str;
public:MyString(const char* p = nullptr) :str(nullptr) //构造函数{if (nullptr != p) //先判断是否为空{int len = strlen(p);//计算字符串的长度str = new char[len + 1];//使用new申请空间——堆区申请空间 (1)sizeof计算大小 (2)malloc (3)构造 (4)返回地址strcpy(str, p);//将p的值拷贝到str中}}~MyString()//析构函数,{if (nullptr != str){delete[] str;str = nullptr;}} void Print() const //常方法和普通方法都可以进行调用{if (str != nullptr){cout << str << endl;}}
};
int main()
{MyString s1("yhping");MyString s2(s1);//系统自动调用默认拷贝构造函数进行初始化
}
存在的问题:(浅拷贝引起的堆区内存问题)
由于用户没有实现拷贝构造函数,系统默认调用自身的拷贝构造函数,调用后,s1和s2指向同一块内存空间,在调用析构函数时,s3先释放资源,此时,当s1调用析构函数时,会因为多次释放同一块空间,造成程序崩溃。
如图所示:
1.什么是浅拷贝
直接复制对象的所有成员变量值,对于指针类型只复制指针值(地址),不复制指针指向的内存。
2.特点
①多个对象共享同一块动态内存。
②析构时可能导致多次释放同一块内存,引发悬空指针或内存泄漏。
三、深拷贝(适用于对态资源)
对于上述代码的修改就可以使用深拷贝解决!
1.什么是深拷贝
复制对象的所有成员变量值,对于指针类型,会重新分配内存并复制其指向的内容。
2.特点
①每个对象拥有独立的动态资源副本。
②析构时不会相互影响,避免内存问题。
四、拷贝赋值运算符的深赋值和浅赋值
如果用户没有自定义赋值运算符时,则系统默认提供一个赋值运算符的函数。是一种浅赋值的操作,在语义上等价于按位赋值(逐成员赋值对象)。
如图所示:(默认是浅赋值)
1.浅赋值
(1)定义
赋值时仅复制对象的成员变量值,对于指针类型只复制指针值(地址),不复制指针指向的内存。
(2)特点
-
多个对象共享同一块动态内存。
-
赋值后,原对象和目标对象的指针指向同一内存地址。
-
析构时可能导致多次释放同一块内存,引发悬空指针或内存泄漏。
2.深复制
对于上述代码造成的问题,可以使用户深赋值解决。
(1)定义
赋值时复制对象的所有成员变量值,对于指针类型,会重新分配内存并复制其指向的内容。
(2)特点
-
每个对象拥有独立的动态资源副本。
-
赋值后,原对象和目标对象的指针指向不同内存地址,但内容相同。
-
析构时不会相互影响,避免内存问题。
五、总结
在类型设计中,使用动态内存或使用内核对象时,必须重新实现拷贝构造函数和赋值重载。(深拷贝、深赋值)。