C++ 深拷贝与浅拷贝详解:从原理到实战
更新时间:2025年6月11日
️ 标签:C++ | 深拷贝 | 浅拷贝 | 对象拷贝 | 内存管理 | C++基础
在 C++ 编程中,对象的复制操作看似简单,但其中隐藏的内存管理问题往往是初学者最容易踩的地雷。这篇文章将以循序渐进的方式,从最基础的值拷贝与地址拷贝讲起,详细解读浅拷贝和深拷贝的原理、区别以及应用场景,帮助你构建正确的拷贝语义认识,为后续的类、封装等面向对象知识打下坚实基础
浅拷贝是指在复制对象时,只复制对象的基本数据成员(值拷贝),而对于指针成员,仅复制指针的值(即内存地址),而不复制指针所指向的内存内容。换句话说,源对象和新对象共享同一块动态分配的内存
#include
using namespace std;
class house
{
public:
house(int sum):area(sum)
{
p=&area;
}
~house()
{
}
void where()
{
cout<<"I live in "<<p<<endl;
}
int area;
int* p;
};
int main()
{
house a(100);
a.where();
house b=a;
b.where();
return 0;
}
输出:
I live in 0x7ffe702f1be0
I live in 0x7ffe702f1be0
我们会发现,house 对象a
和对象b
都共享同一块地址,这就是浅拷贝
深拷贝是指在复制对象时,不仅复制对象的基本数据成员,还为指针成员分配新的内存,并将源对象指针指向的内容复制到新分配的内存中。新对象和源对象完全独立,互不影响
输入:
#include
using namespace std;
int main()
{
int a=10;
int b=a;
cout<<"a地址:"<<&a<<endl;
cout<<"b地址:"<<&b<<endl;
return 0;
}
输出:
a地址:0x7fffb62b7a80
b地址:0x7fffb62b7a84
我们会发现a和b是独立分开的,这就是深拷贝
在 C++ 中,类的默认拷贝行为(由编译器提供的默认拷贝构造函数和赋值运算符)是浅拷贝
。如果你的类管理动态内存(如指针成员),默认的浅拷贝可能导致以下问题:
1.悬空指针(Dangling Pointer):
如果两个对象共享同一块内存,当一个对象销毁时释放了这块内存,另一个对象的指针会变成悬空指针,访问它会导致未定义行为。
2.内存泄漏(Memory Leak):
如果没有正确管理动态内存,可能会导致内存无法释放。
3.数据不一致:
修改一个对象的动态内存内容会意外影响另一个对象。
因此,在涉及动态内存的类中,通常需要显式实现深拷贝,以确保对象的独立性和程序的正确性
此时有一个类,里面包含两个变量,一个是面积area,一个是指向area地址的指针p
相当于此时你需要去购买房子,房子的面积为area,此房子所配的钥匙为指针p
假设我们的类是这样写的!假设!
class house
{
public:
house(int sum):area(sum)
{
p=malloc(area);//给所买的房子分配一把钥匙
}
~house()
{
free(p);
p=nullptr;
}
void where()
{
cout<<"I live in "<<p<<endl;
}
int area;//房屋面积
void* p;//指向房子的地址 相当于钥匙
};
如果对象a购买了一处房产,b也想购买一处与a一样大小的房产,下面写法会不会出错?为什么?
#include
using namespace std;
class house
{
public:
house(int sum):area(sum)
{
p=malloc(area);
}
~house()
{
free(p);
p=nullptr;
}
void where()
{
cout<<"I live in "<<p<<endl;
}
int area;
void* p;
};
int main()
{
house a(100);
a.where();
house b=a;//本意是想购买与a房子同样大小的房子
b.where();
return 0;
}
输出:
I live in 0x55ce6707deb0
I live in 0x55ce6707deb0
free(): double free detected in tcache 2!!!!!报错
为什么会错呢?我们仔细看看对象a和对象b的地址,发现都相同,说明此时共享同一块内存地址,当对象a进行析构的时候会将指针置空,但轮到对象b析构的时候,就产生了对空指针再次清除的报错
我们在构造对象b的时候换一种方式就行
house a(100);
a.where();
house b(100);
b.where();
输出:
I live in 0x55efdd689eb0
I live in 0x55efdd68a330
我们修改类,不用mallco去主动为p分配内存
#include
using namespace std;
class house
{
public:
house(int sum):area(sum)
{
p=&area;
}
~house()
{
}
void where()
{
cout<<"I live in "<<p<<endl;
}
int area;
int* p;
};
int main()
{
house a(100);
a.where();
house b=a;
b.where();
return 0;
}
输出:
I live in 0x7ffd4341c5a0
I live in 0x7ffd4341c5a0
但这样并没有解决问题的本质,本质上对象a和对象b还是同一个内存
本质问题是一个深拷贝和浅拷贝的问题,我们要进行深拷贝,要独立地址
所以我们在类里面可以这样修改
#include
using namespace std;
class house
{
public:
house(int sum):area(sum)
{
p=&area;
}
house(const house&a):area(a.area)//深拷贝构造
{
p=&area;
}
~house()
{
}
void where()
{
cout<<"I live in "<<p<<endl;
}
int area;
int* p;
};
int main()
{
house a(100);
a.where();
house b=a;
b.where();
return 0;
}
输出:
I live in 0x7ffdf435f970
I live in 0x7ffdf435f980
这样就解决这个问题了
通过简单的案例来理解深拷贝与浅拷贝,本质上就是是否共享同一个内存区域的问题
浅拷贝 适用于仅含基本数据类型的对象;
深拷贝 适用于含有动态内存或指针的对象,防止资源重复释放或共享;
如果你觉得本文对你有帮助,不妨点赞 + 收藏 + 关注,更多 C++ 系列教程将持续更新 !