5 class Test{
6 public:
7 Test(int n, const char* ch){
8 m_ = n;
9 ch_ = new char[strlen(ch)+1];
10 strcpy(ch_, ch);
11 }
12 ~Test(){
13 cout << "~Test" << endl;
14 if(ch_ != NULL){
15 delete[] ch_;
16 ch_ = NULL;
17
18 }
19 }
20 char* get_addr()const{
21 return ch_;
22 }
23
24 private:
25 int m_;
26 char* ch_;
27 };
28 int main()
29 {
30 Test t1(3, "good");
31 Test t2 = t1;
32 printf("t1 = %p\n", t1.get_addr());
33 printf("t2 = %p\n", t2.get_addr());
37 return 0;
38 }
39
我们以上面的代码为例,当我们调用拷贝构造函数时,如果没有显示的给出拷贝构造函数,则调用默认的拷贝构造函数,而默认的拷贝构造函数是进行浅拷贝的,仅仅简单的完成值的复制。画个图表示以下:
可以看到对象t2仅仅对t1进行了简单的值的复制,ch_指向同一个内存区域,这样会有严重的后果,那就是会对同一个地址进行多次释放,产生段错。
总的来说,如果类的成员变量都在栈上进行分配,那么浅拷贝也是可以满足要求的,但如果是堆上的数据,则会多次发生析构行为。
那我要如何解决这个问题那??那就要进行深拷贝,手工编写拷贝构造函数,并在拷贝构造函数中新申请一块内存,并将值复制过来,将指针指向新开辟的内存区域。画个图示意以下:
修改后的代码如下:
#include <iostream>
2 #include<cstring>
3 #include<cstdio>
4 using namespace std;
5 class Test{
6 public:
7 Test(int n, const char* ch){
8 m_ = n;
9 ch_ = new char[strlen(ch)+1];
10 strcpy(ch_, ch);
11 }
12 Test(const Test& another){
13 m_ = another.m_;
14 ch_ = new char[strlen(another.ch_)+1];
15 strcpy(ch_, another.ch_);
16
17
18 }
19 ~Test(){
20 cout << "~Test" << endl;
21 if(ch_ != NULL){
22 delete[] ch_;
23 ch_ = NULL;
24
25 }
26 }
27 char* get_addr()const{
28 return ch_;
29 }
30
31 private:
32 int m_;
33 char* ch_;
34 };
35 int main()
36 {
37 Test t1(3, "good");
38 Test t2 = t1;
39 printf("t1 = %p\n", t1.get_addr());
40 printf("t2 = %p\n", t2.get_addr());
41
运行结果:
可以看到两个指针指向的内存地址不同。
ps:在vscode下并不会出现这个错误,不知道为啥??要大佬指点以下码??
补充有关匿名对象的有趣现象
匿名对象啥时候会出现?
Test go(){
Test en;
return en;
}
这种情况下会返回一个匿名对象,同时会调用匿名对象的拷贝构造函数。
比较有趣的是到底该匿名对象什么时候会析构???
- 如果用匿名对象来初始化另一个同类型的对象A,匿名对象被转化为有名对象A,被扶正了,只会在该对象A被销毁时调用一次析构函数。
- 如果用匿名对象赋值给另一个同类型的对象B,匿名对象立刻被析构,而对象B在销毁时调用一次析构函数。