12.2.5 深拷贝与浅拷贝

    技术2025-05-31  9

    12.2.5 深拷贝与浅拷贝

    之前说过,一个类被创建时,编译器会自动生成一个拷贝构造函数,这个拷贝构造函数就是把传入的一个类对象的各个属性世界复制到新创建的对象上。例如一个person类,属性如下:

    注意m_Height是指针。 int m_Age; int *m_Height;

    这时,编译器自动提供的拷贝构造函数原型其实是这样的:

    person(const person &p) { m_Age = p.m_Age; m_Height = p.m_Height; }

    这样也就带来了以一问题:新创建的对象的m_Height和原来的对象的m_Height指向同一块内存空间。 看一下图解: step1 我们创建一个对象p1,设置年龄15,身高160,且身高是开辟在堆区的指针对象。 step2 再用默认的拷贝构造函数创建一个p2,这样系统会执行这样的代码来初始化各个属性:

    m_Age = p.m_Age; m_Height = p.m_Height;

    这样的话,p1和p2的身高是指向同一块内存的指针。 这时,如果把他俩其中任何一个的m_Height释放了,另一个m_Height就成了野指针,不光对数据的操作是非法的,释放内存时也会因为同一块内存释放两次而使程序崩溃。 编译器的这种拷贝方式成为浅拷贝。 我们来演示一下这个问题:

    #include<iostream> using namespace std; class person { public: person() { } person(int age, int height) { m_age = age; m_Height = new int(height); } //下面是模仿编译器自动生成的拷贝构造函数,是浅拷贝 person(const person& p) { m_age = p.m_age; m_Height = p.m_Height; } ~person() { if (m_Height != NULL) { delete m_Height; } } int m_age; int* m_Height; }; void test1() { person p1(18, 160); person p2(p1); } int main() { test1(); system("pause"); return 0; }

    调试运行时,底层delete的实现代码出错了,原因就是一块内存被释放了两次。 事实上,是p2正常释放,再释放p1时出错。因为p1和p2都是局部变量,创建在栈区,栈区的对象都是“先进后出”,p1先创建,所以后释放。 那么怎么避免这种浅拷贝带来的问题呢? 我们只需要重写拷贝构造函数,并且不让指针对象只是做地址的浅拷贝就行了。 我们把上面的拷贝构造函数改写成下面这样:

    person(const person& p) { m_age = p.m_age; m_Height = new int(*p.m_Height); }

    这样,相当于在创建p2时重新在堆区开辟一块内存空间,单独存放p2的身高数据。这样p1和p2的身高不共享同一块内存,就不会出现释放两次出错的问题了。 这种拷贝方式称为深拷贝。 深拷贝创建的对象,和原对象没有任何依存关系,可以独立操作,互不影响。

    Processed: 0.010, SQL: 9