复制构造函数+浅复制和深复制

时间:2020-08-02 18:11:19

标签: c++ pointers copy-constructor deep-copy shallow-copy

我想问的是,当我不显式编写任何副本构造函数时,编译器会自动生成默认情况下执行浅表复制的副本构造函数吗? 因此,在main()程序中,当我更改整数a,b和指针p的值时,只有p的值发生了更改,而a和b的值在复制的对象中保持不变。为什么a和b的值也没有变化?我的代码是:

#include <iostream>
#include <string.h>
using namespace std;

class Dummy {
    private:

        int a, b;
        int *p;
    public:
        Dummy() {
            p = new int;
        }
        void setData(int x, int y, int z) {
            a = x;
            b = y;
            *p = z;

        }
        void showData() {
            cout << "a = " << a << " b = " << b;
            cout << " p = " << *p << endl;
        }

        ~Dummy() {
            delete p;
        }
};

int main() {

    Dummy d1;
    d1.setData(3, 4, 5);
    Dummy d2 = d1;
    d1.showData();
    d2.showData();
    d1.setData(6, 7, 8);
    d1.showData();
    d2.showData();
    return 0;
}

我的程序的输出是:

a = 3 b = 4 p = 5
a = 3 b = 4 p = 5
a = 6 b = 7 p = 8
a = 3 b = 4 p = 8

我的意思是,当我更改对象d1的值时,对象d2的指针发生了变化,那么为什么对象d2的a和b的值也没有变化?

我也在析构函数中使用delete关键字删除动态分配的指针:

~Dummy() {
            delete p;
        }

但是它使我的程序崩溃了。为什么呢?

1 个答案:

答案 0 :(得分:2)

您完全错了-The idea of shallow copy。实际上,c++本身没有内置任何称为deep copy的东西。因此,称shallow copya bit wrong。而且仅使用这些单词shallow copy也会创建很多confusion

现在,让我解释一下,当cpp执行initialization using assignment时会发生什么。 cppc(在复制struct时)有一个称为bitwise copy的概念。在这个概念中,all the member variables of one object(struct object/class object - you can say either) is identically copied to another object。现在,totally wrong ideaboth objects point to same memory location。实际上,both object拥有own memory location,当然their variables占据different memory spaces。对于您来说,我已经编写了一些有关内存的测试。如果您仅看到测试及其输出,便会完全理解:

#include <iostream>
#include <string.h>

using namespace std;


class Dummy {
    int a, b;
    int *p;
public:
    Dummy() {
        p = new int;
    }
    void setData(int x, int y, int z) {
        a = x;
        b = y;
        *p = z;
    }
    void showData() {
        cout << "a = " << a << " b = " << b;
        cout << " p = " << *p << endl;
        cout << endl; // an extra new line for readability of output
    }
    void showMemory() {
        cout << "addr(a) = " << &a << " addr(b) = " << &b;
        cout << " addr(p) = " << &p << endl;
    }
    ~Dummy() {
        *p = 100;
        delete p;
    }
};
// testing memory
void memoryTest() {
    cout << "testing d1:" << endl;

    Dummy d1;
    d1.setData(3, 4, 5);
    
    cout << "addr(d1) = " << &d1 << endl;
    d1.showMemory();
    cout << endl ;


    cout << "testing d2:" << endl;

    Dummy d2 = d1;
    cout << "addr(d2) = " << &d2 << endl;
    d2.showMemory();
}

int main() {
    // memoryTest
    memoryTest();

    return 0;
}

测试的输出是:

testing d1:
addr(d1) = 0x6dfed4
addr(a) = 0x6dfed4 addr(b) = 0x6dfed8 addr(p) = 0x6dfedc

testing d2:
addr(d2) = 0x6dfec8
addr(a) = 0x6dfec8 addr(b) = 0x6dfecc addr(p) = 0x6dfed0

这清楚地表明,这两个对象d1d2占用的内存完全不同。

  1. 现在,您可能还有另一个问题:然后,为什么当我写*p=8时,它同时影响d1d2

当您分配Dummy d2 = d1;时,我们可能会说类似下面的事情(尽管,应用按位复制实际上并没有发生,只是为了清楚起见):

d2.p = d1.p

因此,我们知道d1.pd2.p包含相同的内存位置(注意:d1.p是指针。因此,它不包含任何整数,而包含的内存地址为一个int)。

因此,当您编写*p = 8时,是在告诉程序转到p指定的存储位置,并将该存储位置的值更改为8。(请注意,此处您没有更改d1.pd1.p的内容仍然包含相同的存储位置,只是您将该存储位置的内容从5更改为8)。这就是为什么当您调用d2.p时会得到更改的值。原因,d2.pd1.p包含相同的内存位置。

  1. 现在,可能还有一个问题:为什么在析构函数中释放p时代码会崩溃?

现在,让我首先问您,您可以释放一个已经释放的内存吗?您可以编写代码,但是行为是不确定的。它可能会使您的程序崩溃,也可能什么都不做。

好吧,您在Dummy destructor中写了delete p;。现在,d2d1将首先被销毁。假设d2被销毁了first。因此,当调用d2's驱逐舰时,pfreed。然后,将调用d1's驱逐舰,并且还将尝试free p。但是p已被释放。就您而言,由于这个原因,程序会崩溃。

希望,现在一切都清楚了。

如果我对上述内容不清楚,请提出问题,我也会尽力回答。