C++面试题(17)| 深浅拷贝的区别

109 阅读3分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第10天,点击查看活动详情

image.png

(欢迎大家关注我的微信公众号——控制工程研习,上面会分享很多我学习过程中总结的笔记。)

区别:

浅拷贝深拷贝
1在浅拷贝中,存储原始对象的副本,最终仅复制引用地址在深拷贝中,将存储原始对象的副本和重复的副本。
2浅拷贝比深拷贝更快。深拷贝比浅拷贝慢。
3在复制的对象中所做的更改也会反映在原始对象上,因为两个对象共享同一块内存在复制的对象中进行更改时,原始对象上不会反射。
4它将对象的引用存储在主内存中。它存储对象值的副本。

定义:

· 浅拷贝是按位拷贝对象,它会创建一个新对象,这个对象有着原始对象属性值的一份精确拷贝。

· 深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。

C++实验:

#include <iostream>
using namespace std;

class Test
{
public:
    int a;
    int* p;
    Test(int x) //  parameterized constructor
    {
        a = x;
        p = new int[a];
    }
    Test(Test& t)// copy constrcutor
    {
        a = t.a;
        p = t.p;
    }
};

int main()
{
    Test t(3);
    Test t2(t);
}

在这里,我们有一个名为 Test的类,它有两个构造函数,即一个参数构造函数 Test(int x) 和一个拷贝构造函数 Test(Test& t) ,因为我们可以在一个类中定义多个构造函数,即构造函数重载。

    在main()函数中,t对象使用参数构造函数构造,同时传入了参数3给x。该对象如下:

图片

    t2对象则通过拷贝构造函数,拷贝了t对象:

图片

    注意这个拷贝构造函数的操作结果:我们首先创建的对象“t”。因此,t2.a 分配为 3。然后,t2.p 分配为 t.p,所以 t2.p和t.p 将指向相同的内存地址。这就是浅拷贝。

    这样是没有满足设计需求的(除非你的需求就是这样)。t2 对象应该有自己的的数组。这并不符合实际的操作需求。

    因此,拷贝构造函数的问题在于,如果一个对象完成了动态内存分配(堆部分中的内存分配),那么拷贝构造函数将不会为其创建新的内存。它将指向相同的内存。所以,你必须小心这种事情。

    所以,我们应该修改拷贝构造函数。方法就是在拷贝构造函数里直接创造一个新的数组,然后把被复制对象的数组里的值一个一个赋值过来:

#include <iostream>
using namespace std;

class Test
{
    public:
    int a;
    int *p;

    Test (int x)
    {
        a = x;
        p = new int[a];
    }
    Test (Test & t)
    {
        a = t.a;
        p = new int[a];
        if (p)
        {
            for (int i = 0; i < a; i++)
            {
                p[i] = t.p[i];
            }
        }
    }
};

int main()
{
    Test t (5);
    t.p[0] = 1;
    Test t2 (t);
    t2.p[0] = 5;
    cout << "t: " << t.a << " " << t.p[0] << endl;
    cout << "t2: " << t2.a << " " << t2.p[0] << endl;
}

运行结果:

图片

    这里t2对象中的p数组修改了值,也没有影响到t对象的参数值。这就是实现了深拷贝。所以需要注意的是,在复制数组/指针之类的参数时要注意这个问题,因为只是单纯的值传递不会容易出现错误。