C++之深拷贝与浅拷贝
C++是一门强大的编程语言,通过使用对象来完成数据的封装,使得我们能够更好地管理程序的状态和行为。在C++中,拷贝是开发人员常用的一种方式,可以复制对象并在不同的位置使用。拷贝类型分为浅拷贝和深拷贝,下面我们来详细的了解一下这两种拷贝。
浅拷贝和深拷贝的定义
浅拷贝是将源对象的指针直接复制到目标对象中,由于源与目标共享一份数据,当一份数据的值被改变时,源和目标都会发生改变;深拷贝是新创建一个对象,并将源对象的所有数据副本复制到新对象中,由于源与目标是完全独立的,不会再次发生改变。
使用方法
浅拷贝通常在类中的成员函数中使用,使用时会简单地将源对象的数据成员赋值给目标对象。
深拷贝会使用动态内存分配,需要自己手动管理内存。
区别
浅拷贝只是将源对象的指针复制到目标对象中,如果源对象的指针所指向的内容发生了改变,那么目标对象也会受到影响。相反的,深拷贝会在堆内存中创建一个独立的对象,两个对象互不干扰。
下面,我们来看一个例子:假设我们有一个类T,类内有一个函数GetName(),用于返回类的成员变量m_name的值。
class T {
public:
char* GetName() {
return m_name;
}
void SetName(char* name) {
strcpy(m_name, name);
}
private:
char* m_name;
};
在这个例子中,m_name是一个字符指针,用于存储名字的字符串。我们需要拷贝T类的对象,然后修改其中一个对象的m_name。如果我们使用浅拷贝,代码示例如下:
T obj1;
obj1.SetName("Tom");
T obj2 = obj1;
obj2.SetName("Jerry");
这个例子中,浅拷贝会使得obj1和obj2指向相同的字符串,当我们修改obj2的m_name时,obj1的m_name也会发生改变。如果我们使用深拷贝,代码示例如下:
T obj1;
obj1.SetName("Tom");
T obj2;
*obj2 = *obj1;
obj2.SetName("Jerry");
在这个例子中,深拷贝使得obj1和obj2都有各自独立的m_name,修改obj2的m_name时不会影响到obj1。
注意使用拷贝时的一些事项
浅拷贝只会复制指针,不会复制对象本身,所以需要自己管理内存;深拷贝会复制对象本身到堆内存中,但也需要自己管理内存。
在设计类的时候,需要考虑到拷贝的问题。如果类中拥有指针成员(如本例中的m_name)时,需要注意进行深拷贝或在拷贝构造函数中对其进行复制。此外,类中还可以通过私有化拷贝构造函数和赋值构造函数以实现拒绝拷贝。
案列
再写一个简单的demo,方便理解
#include <iostream>
using namespace std;
/*
浅拷贝:简单的赋值拷贝
深拷贝:在堆区重新申请一块空间,进行拷贝
*/
class Person
{
public:
Person() //默认(无参)构造函数
{
cout << "Person的默认构造函数" << endl;
}
Person(int a,int h) //有参构造函数
{
age = a;
heigh = new int(h);
cout << "Person的有参构造函数" << endl;
}
Person(const Person &p) //拷贝析构函数:相当于把Person &p 拷贝赋值给Person。不能被改变,所以类型相同,加上const,引用。
{
age = p.age;
// heigh = p.heigh; //编译器默认这样,只会析构一次,浅拷贝
heigh = new int(*p.heigh); //p1和p2各自析构一次
cout << "Person的拷贝构造函数" << endl;
}
~Person()
{
if(heigh != NULL){
delete heigh;
heigh =NULL;
}
cout << "Person的析构函数"<< endl;
}
int age;
int *heigh;
};
int main()
{
Person p1(18,170); //编译器会做浅拷贝,把“18”逐位拷贝到P2
cout <<"p1的年龄为: " <<p1.age<<" 身高为:"<<*p1.heigh<< endl;
Person p2(p1);
cout <<"p2的年龄为: " <<p2.age<<" 身高为:"<<*p2.heigh<< endl;
//编译器会做浅拷贝,把“18”逐位拷贝到P2
//指针堆区开辟了一块内存,指针变量, 存放“170”,拷贝了同一块内存给P2
//释放栈的规则,先进后出,p2先被释放,"170"的内存已经被释放了,p1又释放一次,非法操作
//浅拷贝会造成堆区的内存重复释放
//深拷贝:重新在堆区找一块空间,p1和p2的值相同但是地址不同
return 0;
}
总结
深拷贝和浅拷贝都有各自的优缺点,开发人员需要根据具体情况选择最适合的方式。在使用时,需要注意内存管理问题以避免出现内存泄漏导致程序崩溃的问题。