先说下自己的认知吧,浅复本,即在表述两个类A,采用类似于A obj; A obj1(obj);或是A obj1 = obj; 这时候,虽然没自订复本缺省,C++C++手动会造成两个预设的复本构造函数。那个预设的复本缺省选用的是“位复本”(浅复本),而非“值复本”(深复本)的形式,倘若类中所含操作形式符表达式,预设的复本缺省倘若手忙脚乱。
用句单纯不然而言是浅复本,而已对操作形式符的复本,复本后三个操作形式符对准同两个缓存内部空间,深复本不仅对操作形式符展开复本,因此对操作形式符对准的文本展开复本,经深复本后的操作形式符是对准三个相同门牌号的操作形式符。
浅复本会再次出现甚么难题呢?
倘若有两个核心成员表达式的操作形式符,char *m_data;
一是,浅复本而已复本了操作形式符,使三个操作形式符对准同两个门牌号,这种在第一类块完结,eval析构的时,会引致同两个份天然资源析构2次,即delete同两个块缓存2次,引致流程崩盘。
其三,浅复本使obj.m_data和obj1.m_data对准同两个块缓存,一方的发生变动单厢负面影响到一方。
其三,在释放出来缓存的这时候,会引致obj1.m_data旧有的缓存没被释放出来(这句话,刚开始我不太认知,倘若没走自订的复本缺省,提出申请缓存内部空间,A obj1(obj);也不走预设缺省,走的是预设的复本缺省,惟有重新分配内部空间嗯,更无法引致obj1.m_data旧有的缓存没被释放出来,这儿刚开始我始终有疑点),引致缓存外泄。
历史事实是这样的,当delete obj.m_data, obj.m_data缓存被释放出来后,虽然以后obj.m_data和obj1.m_data对准的是同两个缓存内部空间,obj1.m_data所称的内部空间无法在被借助了,delete obj1.m_data也无法获得成功,完全一致已经难以操作形式该内部空间,因此引致缓存外泄。
深复本选用了在堆缓存中提出申请新的内部空间来存储数据,这种每个可以避免操作形式符悬挂。
下面来看看类string的复本构造函数
class String { public: String(const String &other); //复本缺省 private: char *m_data; //用于保存字符串}; String(const String &other) { int length = strlen(other.m_data); m_data = new char[length +1]; strcpy(m_data, other.m_data); }可以看到在复本缺省中为核心成员表达式提出申请了新的缓存内部空间,这就使三个第一类的核心成员表达式不对准同两个缓存内部空间,除非你的确需要这种做,用于实现一些其他的用途。
【文章福利】需要C/C++ Linux服务器架构师学习资料加群812855908(资料包括C/C++,Linux,golang技术,Nginx,ZeroMQ,MySQL,Redis,fastdfs,MongoDB,ZK,流媒体,CDN,P2P,K8S,Docker,TCP/IP,协程,DPDK,ffmpeg等)
浅复本:也是在第一类复制时,而已对第一类中的数据核心成员展开单纯的赋值,倘若第一类中存在动态核心成员,即操作形式符,浅复本就会再次出现难题,下面代码:
#include <stdio.h> class A { public: A() // 缺省,p对准堆中重新分配的一内部空间 { m_data = new char(100); printf(“预设缺省\n”); } ~A() // 析构函数,释放出来动态重新分配的内部空间 { if(m_data != NULL) { delete m_data; m_data = NULL; printf(“析构函数\n”); } } private: char *m_data; // 一操作形式符核心成员 }; int main() { A a; A b(a); // 复制第一类 return 0; }运行结果:
*** glibc detected *** ./simple: double free or corruption (fasttop): 0x000000000c62a010 ***
分析:虽然没复本缺省,走C++预设的复本缺省,A b(a); 展开第一类析构时,会引致释放出来同两个缓存内部空间2次,引致缓存外泄。
深复本:对于深复本,针对核心成员表达式存在操作形式符的情况,不仅仅是单纯的操作形式符赋值,而是重新重新分配缓存内部空间,如下:
#include <stdio.h> #include <string> class A { public: A() // 缺省,p对准堆中重新分配的一内部空间 { m_pdata = new char(100); printf(“预设缺省\n”); } A(const A& r) { m_pdata = new char(100); // 为新第一类重新动态重新分配内部空间 memcpy(m_pdata, r.m_pdata, strlen(r.m_pdata)); printf(“copy缺省\n”); } ~A() // 析构函数,释放出来动态重新分配的空间 { if(m_pdata != NULL) { delete m_pdata; printf(“析构函数\n”); } } private: char *m_pdata; // 一操作形式符核心成员 }; int main() { A a; A b(a); // 复制第一类 return 0; }下面是我在具体的应用中采用深复本的情况,现在把那个demo贴出来:
#include <iostream> #include <errno.h> #include <vector> #include <stdio.h> using namespace std; /*存储记录信息的结构体*/ typedef struct _RECODER_VALUE_STRU { int Id; int Age; }RECODER_VALUE_STRU; class recorder { public: recorder() { m_stru_RecValue.Id =-1; m_stru_RecValue.Age =-1; m_pRecValue = &m_stru_RecValue; m_paddr = new char[100]; memset(m_paddr,0x00 ,100); printf(“预设 construct recorder->&m_stru_RecValue: %x,\t m_pRecValue: %x\t m_paddr: %x\n”, &m_stru_RecValue, m_pRecVa lue, m_paddr); } //复本缺省 /* recorder(const recorder &recorder) { m_stru_RecValue.Id =-1; m_stru_RecValue.Age = -1; m_stru_RecValue = recorder.m_stru_RecValue; m_pRecValue = &m_stru_RecValue; m_paddr = newchar[100]; memset(m_paddr, 0x00 ,100); memcpy(m_paddr, recorder.m_paddr, strlen(recorder.m_paddr)); printf(“复本 construct recorder->&m_stru_RecValue: %x\t m_pRecValue: %x\t m_paddr: %x\n”,&m_stru_RecValue, m_pRecValu e, m_paddr); } */ //构造函数 recorder(int iId, int iAge) { m_stru_RecValue.Id = iId; m_stru_RecValue.Age = iAge; m_pRecValue = &m_stru_RecValue; m_paddr = newchar[100]; memset(m_paddr, 0x00 ,100); memcpy(m_paddr, &iAge, sizeof(int)); printf(“construct recorder->&m_stru_RecValue: %x \t m_pRecValue: %x\t m_paddr: %x\n”, &m_stru_RecValue, m_pRecValue, m_paddr); } ~recorder() { // cout<<“recorder 析构”<<endl; /*if(m_paddr != NULL) { delete m_paddr; m_paddr =NULL; }*/ } public: RECODER_VALUE_STRU m_stru_RecValue;//存储记录信息的结构体 void* m_pRecValue;//每条记录的值char*m_paddr; }; int main() { cout <<“测试预设缺省”<<endl<<endl; recorder btest; recorder btest1(btest); printf(“非参:btest ->&m_stru_RecValue: %x\t addr: %x\t m_paddr: %x\n”, &btest.m_stru_RecValue, btest.m_pRecValue, btest.m_paddr); printf(“非参:btest1->&m_stru_RecValue: %x\t addr: %x\t m_paddr: %x\n”, &btest1.m_stru_RecValue, btest1.m_pRecValue, btest1.m_paddr); cout << endl<<“测试带参数的缺省”<<endl<<endl; recorder btest2(1, 100); recorder btest3(btest2); printf(“带参:btest2->m_stru_RecValue: %x\t m_pRecValue: %x\t, m_paddr: %x\n”, &btest2.m_stru_RecValue, btest2.m_pRecValue, btest2.m_ paddr); printf(“带参:btest3->m_stru_RecValue: %x\t m_pRecValue: %x\t, m_paddr: %x\n”, &btest3.m_stru_RecValue, btest3.m_pRecValue, btest3.m_ paddr);return 0; }对比结果:
注释掉自订复本缺省,运行结果:
测试预设缺省
预设 construct recorder->&m_stru_RecValue: ddbb8de0, m_pRecValue: ddbb8de0 m_paddr: 1b8a0010
非参:btest ->&m_stru_RecValue: ddbb8de0 addr: ddbb8de0 m_paddr: 1b8a0010
非参:btest1->&m_stru_RecValue: ddbb8dc0 addr: ddbb8de0 m_paddr: 1b8a0010
测试带参数的缺省
construct recorder->&m_stru_RecValue: ddbb8da0 m_pRecValue: ddbb8da0 m_paddr: 1b8a0080
带参:btest2->m_stru_RecValue: ddbb8da0 m_pRecValue: ddbb8da0 , m_paddr: 1b8a0080
带参:btest3->m_stru_RecValue: ddbb8d80 m_pRecValue: ddbb8da0 , m_paddr: 1b8a0080
预设复本缺省结果分析:
通过结果可以看出,当核心成员表达式为操作形式符表达式的这时候,操作形式符核心成员表达式对准的门牌号都是同两个门牌号,无论是提出申请内部空间的核心成员表达式m_pRecValue,和仅仅作为操作形式符赋值的核心成员表达式m_paddr;结构体的门牌号是变化的,除了操作形式符浅复本与深复本没甚么区别。
打开自订复本缺省,运行结果:
测试预设缺省
预设 construct recorder->&m_stru_RecValue: 58bb9e20, m_pRecValue: 58bb9e20 m_paddr: 7a2c010
复本 construct recorder->&m_stru_RecValue: 58bb9e00 m_pRecValue: 58bb9e00 m_paddr: 7a2c080
非参:btest ->&m_stru_RecValue: 58bb9e20 addr: 58bb9e20 m_paddr: 7a2c010
非参:btest1->&m_stru_RecValue: 58bb9e00 addr: 58bb9e00 m_paddr: 7a2c080
测试带参数的缺省
construct recorder->&m_stru_RecValue: 58bb9de0 m_pRecValue: 58bb9de0 m_paddr: 7a2c0f0
复本 construct recorder->&m_stru_RecValue: 58bb9dc0 m_pRecValue: 58bb9dc0 m_paddr: 7a2c160
带参:btest2->m_stru_RecValue: 58bb9de0 m_pRecValue: 58bb9de0 , m_paddr: 7a2c0f0
带参:btest3->m_stru_RecValue: 58bb9dc0 m_pRecValue: 58bb9dc0 , m_paddr: 7a2c160
自订深复本缺省结果分析:
从结果可以看出,所有核心成员表达式的门牌号都不相同。
其他:
1. 有这时候为了防止预设复本发生,可以声明两个私有的复本缺省(不用写代码),这种不然,倘若试图调用 A b(a); 就调用了私有的复本缺省,C++会报错,这也是一种偷懒的做法。
2. 两个类中可以存在多个复本缺省,例如:
Calss A { Public: X(const X&);//const复本构造 X(X &);//非const复本构造 X(X& , int iData); }暂时就先分析到这儿,倘若以后遇到新的关于复本构造的情况,再继续分析。