电商平台网站,网页制作与设计html,番禺人才网入库考试,深圳龙华租房什么情况下会用到c中的拷贝构造函数】#xff1a; 1#xff09;用已经存在的同类的对象去构造出另一个新的对象 2#xff09;当函数的形参是类的对象时#xff0c;这时调用此函数#xff0c;使用的是值的拷贝#xff0c;也会调用拷贝构造函数 3#xff09;当函数的返… 什么情况下会用到c中的拷贝构造函数】 1用已经存在的同类的对象去构造出另一个新的对象 2当函数的形参是类的对象时这时调用此函数使用的是值的拷贝也会调用拷贝构造函数 3当函数的返回值是类的对象时这时当函数调用完后会将函数的对象拷贝构造出一个临时的对象并传给函数的返回处 【浅拷贝】(位拷贝值拷贝) 1、概念所谓的浅拷贝就是当在进行对象的复制时只是进行对象的数据成员的拷贝其中默认的拷贝构造函数也是浅拷贝。大多数情况下使用浅拷贝是没有问题的但是当出现动态成员就会出现问题。 2、关于浅拷贝的使用举例: [plain] view plain copy #includeiostream using namespace std; class Test { public: //构造函数 Test(int a) :_a(a) {} //拷贝构造函数 Test(const Test x) { _a x._a; } private: int _a; }; int main() { Test b(10); Test c(b); return 0; } 3、浅拷贝的缺陷 浅拷贝对于指针成员不可行。多个对象共用同一块空间同一内存地址但是在调用析构函数释放空间的时候多次调用析构函数这块空间被释放了多次此时程序就会崩溃。 【引用计数的拷贝】 1、怎么引入的概念因为浅拷贝的缺陷所以在这个时候我们就引入了引用计数的拷贝。 【说明】引用计数的拷贝是用来解决浅拷贝存在的问题的所以它也是一种浅拷贝 2、如何实现我们为每个内存的字符数组添加一个引用计数pcount,即就是在构造函数申请空间的时候多申请出来4个字节。表示有多少个对象使用这块内存有多少个对象使用就让pcount值加1当对象被析构的时候让pcount值减1当pcount值为0的时候将这块内存释放掉。当然pcount也要实现内存共享所以它也是一个堆中的数据每个对象都有一个指向它的指针。 3、【说明】但在此时,pcount的类型的选取就会要有所考虑 1如果选取int类型不采取 [cpp] view plain copy #includeiostream using namespace std; class String { public: //构造函数 String(const char* ptr ) { if(ptr NULL) { _ptr new char[1]; _pcount 1; *_ptr \0; } else { _pcount 1; _ptr new char[strlen(ptr)1]; strcpy(_ptr,ptr); } } //拷贝构造函数 String(String s) :_ptr(s._ptr) ,_pcount(s._pcount) { _pcount; } //赋值运算符重载 String operator(const String s) { if(this ! s) { if(--_pcount 0) { delete[] _ptr; //delete _pcount; } else { _ptr s._ptr; _pcount s._pcount; (_pcount); } } return *this; } //析构函数 ~String() { if((0 --_pcount) _ptr! NULL) { delete[]_ptr; //delete _pcount; _ptr NULL; } } //重载[] char operator[](size_t size) { if(--_pcount 1) { char* ptemp new char[strlen(_ptr)1]; int pcount 1; strcpy(ptemp,_ptr); _pcount--; _ptr ptemp; _pcount pcount; } return _ptr[size]; } private: char*_ptr; int _pcount; }; void FunTest() { String s1(hello); String s2(s1); String s3(s2); s3 s2; } int main() { FunTest(); return 0; } 调试注意这里我将断点就走到s2意在说明问题本来增加s2的时候两个对象的计数应该是一样的但是现在一个是1一个是2不同步我们了解到这两个对象的计数变量的地址是不一样的。说明此pcount是公共的可以被多个对象同时访问。 2如果选取的是static类型的不采取 [cpp] view plain copy #includeiostream using namespace std; class String { public: //构造函数 String(const char* ptr ) { if(ptr NULL) { _ptr new char[1]; _pcount 1; *_ptr \0; } else { _pcount 1; _ptr new char[strlen(ptr)1]; strcpy(_ptr,ptr); } } //拷贝构造函数 String(String s) :_ptr(s._ptr) { _pcount; //因为是静态的所以直接进行计数的增值就可以了 } //赋值运算符重载 String operator(const String s) { if(this ! s) { if(--_pcount 0) { delete[] _ptr; //delete _pcount; } else { _ptr s._ptr; _pcount s._pcount; (_pcount); } } return *this; } //析构函数 ~String() { if((0 --_pcount) _ptr! NULL) { delete[]_ptr; //delete _pcount; _ptr NULL; } } //重载[] char operator[](size_t size) { if(--_pcount 1) { char* ptemp new char[strlen(_ptr)1]; int pcount 1; strcpy(ptemp,_ptr); _pcount--; _ptr ptemp; _pcount pcount; } return _ptr[size]; } private: char*_ptr; static int _pcount; }; int String::_pcount 0; void FunTest() { String s1(hello); String s2(s1); String s3(s2); s3 s2; String s4(world); String s5(s4); } int main() { FunTest(); return 0; } 调试先走到s3然后走到s4用s4来构造s5,结果就不对了走到s4的时候计数器又变成了1说明这5个对象公用一个pcount不能实现引用计数 3那么我们这样想如果一个对象第一次开辟空间存放字符串再开辟一块新的空间存放新的额引用计数当它拷贝构造其它对象时让其它对象的引用计数都指向存放引用计数的同一块空间pcount设置成int*就可以啦但是这种方式有缺陷。读者可以自己实现下 缺陷一每次new两块空间创建多个对象的时候效率比较低 缺陷二它多次分配小块空间容易造成内存碎片化导致分配不出来大块内存 4、代码实现所以我将优化版的代码贴出来其实就是仿照new的底层实现开辟一块空间但是它的头几个字节用于计数 [cpp] view plain copy span stylefont-size:18px;#includeiostream using namespace std; class String { public: String(char *ptr ) { if(ptr NULL) { _ptr new char[strlen(ptr)5]; _ptr new char[5]; *(_ptr4) \0; } else { _ptr new char[strlen(ptr)5]; *((int*)_ptr) 1; _ptr 4; strcpy(_ptr,ptr); } } String(const String s) :_ptr(s._ptr) { (*((int*)(_ptr-4))); } String operator(const String s) { if(this ! s) { if(--(*((int*)(_ptr-4))) 0) { delete[]_ptr; } else { _ptr s._ptr; (*(int*)(_ptr-4)); } } return *this; } ~String() { if((_ptr ! NULL) ((--(*((int*)(_ptr-4)))) 0)) { delete[]_ptr; _ptr NULL; } } private: char* _ptr; }; void Funtest() { String s1(hello); String s2(s1); String s3(s2); s3 s2; } int main() { Funtest(); return 0; }/span 【深拷贝】址拷贝 1、概念采取在堆中申请新的空间来存取数据这样数据之间相互独立。址拷贝。 2、举例string类中的拷贝构造函数 [cpp] view plain copy span stylefont-size:18px;String(const String s) { _ptr new char[strlen(s._ptr)1]; strcpy(_ptr,s._ptr); }/span 【写时拷贝】 1、如何引入的概念但是当其中一个对象改变它的值时其他对象的值就会随之改变所以此时我们采取这样一种做法就是写时拷贝。 2、核心思想 写入时拷贝如果有多个调用者同时要求相同资源如内存或磁盘上的数据存储它们会共同获取相同的指针指向的资源直到某个调用者试图修改资源的内容时系统才会真正复制一份专用副本给该调用者而其他调用者所见到的最初的资源仍然保持不变。这过程中对其他调用者都是透明的。做法的优点如果调用者没有修改该资源就不会有副本被创建因此多个调用者只是读取操作时可以共享同一份资源 写时拷贝指用浅拷贝的方法拷贝其他对象多个指针指向同一块空间只有当对其中一个对象修改时才会开辟一个新的空间给这个对象和它原来指向同一空间的对象不会收到影响。 3、做法给要改变值的那个对象重新new出一块内存然后先把之前的引用的字符数据复制到新的字符数组中这就是写时拷贝。注意同时还要把之前指向的内存的引用计数减1因为它指向了新的堆中的字符数组并在堆中重新new一个块内存用于保存新的引用计数同时把新的字符数组的引用计数置为1。因为此时只有一个对象就是改变值的对象在使用这个内存。 4、代码实现 [cpp] view plain copy span stylefont-size:18px;#includeiostream using namespace std; class String { public: //构造函数 String(const char* ptr ) { if(ptr NULL) { _ptr new char[1]; _pcount new int(1); *_ptr \0; } else { _pcount new int(1); _ptr new char[strlen(ptr)1]; strcpy(_ptr,ptr); } } //拷贝构造函数 String(String s) :_ptr(s._ptr) ,_pcount(s._pcount) { (*_pcount); } //赋值运算符重载 String operator(const String s) { if(this ! s) { if(--(*_pcount) 0) { delete[] _ptr; delete _pcount; } else { _ptr s._ptr; _pcount s._pcount; (*_pcount); } } return *this; } //析构函数 ~String() { if(0 --(*_pcount) _ptr! NULL) { delete[]_ptr; delete _pcount; _ptr NULL; } } //重载[] char operator[](size_t size) { if(--(*_pcount) 1) { char* ptemp new char[strlen(_ptr)1]; int* pcount new int(1); strcpy(ptemp,_ptr); (*_pcount)--; _ptr ptemp; _pcount pcount; } return _ptr[size]; } private: char*_ptr; int* _pcount; }; void FunTest() { String s1(hello); String s2(s1); String s3(s2); s3 s2; String s4(s1); s1[0] y; } int main() { FunTest(); return 0; }/span 经调试结果如下 【注意】因为不止一个对象使用这一块内存当修改自己的时也等于修改了他人的。在向这块存储单元写之前应该确信没有其他人使用它。如果引用计数大于1在写之前必须拷贝这块存储单元这样就不会影响他人了。