C++中的浅拷贝/深拷贝/赋值运算符
·
1、拷贝是什么
通俗的讲,拷贝就是再创建一个一摸一样的对象。
例如:
A b = a;
A b(a);
void foo(A x); // 会拷贝
foo(a); // 产生拷贝
都会触发对象拷贝,默认会进行成员逐个拷贝(浅拷贝)
2、浅拷贝与深拷贝
浅拷贝:类似于复印一本书,只复印了封面,里面的内容没有复印,两个对象共用内容。所以任何一方修改内部内容,另一方也会受到影响。特别是delete的时候,会出错
深拷贝:封面和内容都复制一遍,两个对象互相不影响
例如:
class A {
public:
int* p;
};
浅拷贝的情况下,两个对象指向一块内存。
- double free(释放两次)
- 修改A的p会影响B的p
3、拷贝构造函数
编译器默认是浅拷贝,出现的场景如下:
- 用一个对象创建另一个对象
- 函数传参(按值)
- 函数返回对象(按值)
相当于新建时复制,示例代码如下:
格式:A(const A& other);
#include <iostream>
class MyData {
public:
explicit MyData(const int x) {
p = new int(x);
std::cout<<"构造函数:分配内存,值="<<*p<<"\n";
}
MyData(const MyData &other) {
p = new int(*other.p);
std::cout<<"拷贝构造:深拷贝,值="<<*p<<"\n";
}
~MyData() {
delete p;
std::cout<<"释放内存"<<"\n";
}
int* p;
};
int main() {
MyData a(10);
MyData b = a;
std::cout<<"修改副本b.p的值\n";
*b.p = 20;
std::cout<<"a.p = "<<*a.p<<"\n";
std::cout<<"b.p = "<<*b.p<<"\n";
}
4、复制赋值运算符
operator=用于现成对象被赋值。拷贝构造是又买了一本新书;赋值运算是把旧书的内容擦掉,重新写成另一本书的内容;让已经存在的对象把内容变成另外一个对象的内容。
格式:A& operator=(const A& other);
示例代码如下:
#include <iostream>
class MyData {
public:
explicit MyData(const int x) {
p = new int(x);
std::cout<<"构造函数:分配内存,值="<<*p<<"\n";
}
MyData(const MyData &other) {
p = new int(*other.p);
std::cout<<"拷贝构造:深拷贝,值="<<*p<<"\n";
}
MyData& operator=(const MyData &other) {
if (this == &other) { // 自赋值检查
std::cout << "赋值运算符: 自赋值,跳过\n";
return *this;
}
std::cout << "赋值运算符: 深拷贝, 新值 = " << *other.p << "\n";
// 先释放旧资源
delete p;
// 分配新内存并复制值
p = new int(*other.p);
return *this;
}
~MyData() {
delete p;
std::cout<<"释放内存"<<"\n";
}
int* p;
};
int main() {
MyData a(10); // 构造
MyData b(20); // 构造
// 测试赋值运算符
a = b; // 深拷贝赋值
std::cout << "赋值后: a.p = " << *a.p << ", b.p = " << *b.p << "\n";
// 修改 b,不影响 a
*b.p = 30;
std::cout << "修改 b 后: a.p = " << *a.p << ", b.p = " << *b.p << "\n";
// 测试自赋值
a = a;
}
5、禁止拷贝(=delete)
有些类例如管理文件句柄的类;管理网络连接的类;管理互斥锁的类不允许被拷贝,可以按如下格式写:
ClassName(const ClassName&) = delete;
ClassName& operator=(const ClassName&) = delete;
6、Rule of Three(非常重要)
如果实现了以下三个中的任何一个,则必须实现另外两个:
- 析构函数
- 拷贝构造函数
- 复制赋值运算符(operator=)
否则会产生资源泄漏或者double free。
也就是说如果手动管理资源,必须告诉程序如何赋值和销毁。否则编译器默认浅拷贝会造成内存或资源问题。
更多推荐

所有评论(0)