C++的默认拷贝构造函数,从深拷贝和浅拷贝说起

2023-01-28 0 1,111

1. c++类的预设复本缺省的弊病

c++类的中有三个特定的缺省,(1)无参缺省,(2)复本缺省。它的特定之处是:

(1)当类中没表述任何人缺省时,C++会预设提供更多三个无参缺省且其表达式并有空;

(2)当类中没表述复本缺省时,C++会预设提供更多三个复本缺省,展开核心成员表达式间的复本。(那个复本操作方式是浅复本)

这儿只讲复本缺省。在c词汇中,

int a = 5; //初始化 int b; b = 6; //表达式

下面的初始化及表达式操作方式是最恒定但是的句法,c++词汇母盛氏相容c词汇句法的职责,因此在类的结构设计上,也相容此种操作方式:

class cls { pubic: //… } int main(void) { cls c1; cls c2 = c1; //初始化类,还能 cls c2(c1); cls c3; c3 = c1; //表达式类 //… return 0; }

如上的初始化类须要初始化到cls类的预设同时实现的复本缺省,Sonbhadra表达式须要初始化的是cls类的预设同时实现的表达式操作方式符空载表达式,它都是survive复本的。后者其原型为:

预设的复本缺省存有弊病,看如下表所示类表述:

class TestCls{ public: int a; int *p; public: TestCls() //无参缺省 { std::cout<<“TestCls()”<<std::endl; p = new int; } ~TestCls() //析构表达式 { delete p; std::cout<<“~TestCls()”<<std::endl; } };

类中的操作符p在缺省中重新分配的内部空间,在析构表达式中释放出来。

int main(void) { TestCls t; return 0; }

编译运行确实不会出错:

类在我们没表述复本缺省的时候,会预设表述预设复本缺省,也就是说能直接用同类型的类间能相互表达式、初始化:

int main(void) { TestCls t1; TestCls t2 = t1; //效果等同于TestCls t2(t1); return 0; }

编译通过,运行却出错了:

原因就是,预设的复本缺省同时实现的是浅复本。

2. 深度复本和浅复本

深度复本和浅复本在c词汇中就经常遇到的了,在这儿我简单描述。

一般的表达式操作方式是深度复本:
//深度复本 int a = 5; int b = a;

简单的操作符指向,则是浅复本:

//浅复本 int a = 8; int *p; p = &a; char* str1 = “HelloWorld”; char* str2 = str1;

将下面的浅复本改为深度拷贝后:

//深度复本 int a = 8; int *p = new int; *p = a; char* str1 = “HelloWorld”; int len = strlen(str1); char *str2 = new char[len]; memcpy(str2, str1, len);

稍微有点c词汇基础的人都能看得出深度复本和浅复本的差异。总而言之,复本者和被复本者若是同三个地址,则为浅复本,反之为深复本。

以字符串复本为例,浅复本后,str1和str2同指向0x123456,不管哪三个操作符,对该内部空间内容的修改都会影响另三个操作符。

深复本后,str1和str2指向不同的内存内部空间,各自的内部空间的内容一样。因为内部空间不同,因此不管哪三个操作符,对该内部空间内容的修改都不会影响另一个操作符。

3. 解决预设复本缺省的弊病

类的预设复本缺省只会用被复本类的核心成员的值为复本类简单初始化,也就是说二者的p操作符指向的内存内部空间是一致的。以前面TestCls能知道,C++为我们预设表述的复本缺省为:

TestCls(const TestCls& testCls) { a = testCls.a; p = testCls.p; //三个类的p操作符指向的地址一致。 }

main表达式将要退出时,复本类t2的析构表达式先得到执行,它把自身p指向的堆内部空间释放出来了;接下来,t1的析构表达式得到初始化,被复本类t1的析构表达式得到初始化,它同样要去析构自身的p指向指向的堆内部空间,但是该内部空间和t2类中p指向的内部空间一样,造成重复释放出来,程序运行崩溃。

解决办法十分简单,自表述复本缺省,里面用深度复本的方式为复本类初始化:

class TestCls{ public: int a; int *p; public: TestCls() { std::cout<<“TestCls()”<<std::endl; p = new int; } TestCls(const TestCls& testCls) { std::cout<<“TestCls(const TestCls& testCls)”<<std::endl; a = testCls.a; //p = testCls.p; p = new int; *p = *(testCls.p); //为复本类的p操作符重新分配内部空间,同时实现深度复本 } ~TestCls() { delete p; std::cout<<“~TestCls()”<<std::endl; } }; int main(void) { TestCls t1; TestCls t2 = t1; return 0; }

编译运行恒定:

关于c++复本缺省的深度复本和浅复本的介绍到这儿,其实还能将它的地址打印出来看看,但是这一步就不再赘述了。

c++的复本缺省还有一处妙用,就是自表述复本缺省,并设置为private属性,其同时实现体能什么都不写,那么那个类将变成三个不可被复制的类了。

本文首发于微信公众号 「 LinuxOK 」,ID:L

相关文章

发表评论
暂无评论
官方客服团队

为您解决烦忧 - 24小时在线 专业服务