拷贝构造函数+浅拷贝和深拷贝
Copy constructor + Shallow & deep 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;
}
但它反而让我的程序崩溃了。这是为什么?
你完全错了 - The idea of shallow copy
。实际上,c++
并没有内置任何叫做 deep copy
的东西。所以,调用 shallow copy
是 a bit wrong
。仅仅使用这些词 shallow copy
也创造了很多 confusion
。
现在,让我解释一下,当 cpp
执行 initialization using assignment
时会发生什么。 cpp
或 c
(在复制结构时)有一个概念叫做 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 idea
both 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
这清楚地表明,d1
和d2
这两个对象占用的内存是完全不同的。
- 现在,您可能还有另一个问题:那么,为什么当我写
*p=8
时,它会同时影响 d1
和 d2
?:
当您分配 Dummy d2 = d1;
时,我们可能会说发生了如下情况(尽管应用按位复制时实际上并没有发生,只是为了清楚起见):
d2.p = d1.p
所以,我们知道,d1.p
和d2.p
包含相同的内存位置(注意:d1.p是一个指针。所以,它不包含任何整数,而是它包含一个 int 的内存地址)。
因此,当您编写 *p = 8
时,您是在告诉程序转到 p 所指向的内存位置并将该内存位置的值更改为 8。(注意,在这里,您没有更改 d1.p
的内容,d1.p
仍然包含相同的内存位置。相反,您只是将该内存位置的内容从 5 更改为 8)。这就是为什么当您调用 d2.p
时,您会得到更改后的值。因为,d2.p
包含与 d1.p
.
相同的内存位置
- 现在,可能还有一个问题:为什么在析构函数中释放
p
时代码会崩溃?:
现在,让我先问你,你能释放已经释放的内存吗?您可以编写代码,但行为未定义。它可能会使您的程序崩溃或什么都不做。
好吧,在 Dummy
destructor
中你写了 delete p;
。现在,d2
或 d1
将首先被销毁。假设 d2
被销毁 first
。所以,当调用 d2's
destroyer 时,p
就是 freed
。然后,d1's
destroyer 将被调用,它也会尝试 free p
。但是 p
已经被释放了。在您的情况下,程序因此而崩溃。
希望,你现在一切都清楚了。
上面说的有什么不明白的地方再问,我也会尽量解答的
我想问一下,当我没有显式写任何复制构造函数时,编译器会自动生成默认执行浅拷贝的复制构造函数,对吗? 因此在 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;
}
但它反而让我的程序崩溃了。这是为什么?
你完全错了 - The idea of shallow copy
。实际上,c++
并没有内置任何叫做 deep copy
的东西。所以,调用 shallow copy
是 a bit wrong
。仅仅使用这些词 shallow copy
也创造了很多 confusion
。
现在,让我解释一下,当 cpp
执行 initialization using assignment
时会发生什么。 cpp
或 c
(在复制结构时)有一个概念叫做 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 idea
both 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
这清楚地表明,d1
和d2
这两个对象占用的内存是完全不同的。
- 现在,您可能还有另一个问题:那么,为什么当我写
*p=8
时,它会同时影响d1
和d2
?:
当您分配 Dummy d2 = d1;
时,我们可能会说发生了如下情况(尽管应用按位复制时实际上并没有发生,只是为了清楚起见):
d2.p = d1.p
所以,我们知道,d1.p
和d2.p
包含相同的内存位置(注意:d1.p是一个指针。所以,它不包含任何整数,而是它包含一个 int 的内存地址)。
因此,当您编写 *p = 8
时,您是在告诉程序转到 p 所指向的内存位置并将该内存位置的值更改为 8。(注意,在这里,您没有更改 d1.p
的内容,d1.p
仍然包含相同的内存位置。相反,您只是将该内存位置的内容从 5 更改为 8)。这就是为什么当您调用 d2.p
时,您会得到更改后的值。因为,d2.p
包含与 d1.p
.
- 现在,可能还有一个问题:为什么在析构函数中释放
p
时代码会崩溃?:
现在,让我先问你,你能释放已经释放的内存吗?您可以编写代码,但行为未定义。它可能会使您的程序崩溃或什么都不做。
好吧,在 Dummy
destructor
中你写了 delete p;
。现在,d2
或 d1
将首先被销毁。假设 d2
被销毁 first
。所以,当调用 d2's
destroyer 时,p
就是 freed
。然后,d1's
destroyer 将被调用,它也会尝试 free p
。但是 p
已经被释放了。在您的情况下,程序因此而崩溃。
希望,你现在一切都清楚了。
上面说的有什么不明白的地方再问,我也会尽量解答的