C++深拷贝与浅拷贝(实现String类)
来源:互联网 发布:安川伺服软件下载 编辑:程序博客网 时间:2024/06/06 18:56
浅拷贝:
1.什么是浅拷贝? 浅拷贝会出现什么问题?
所谓浅拷贝,指的是在对象复制时,只是对对象中的数据成员进行简单的复制,默认拷贝构造函数执行的也是浅拷贝。简单的说,浅拷贝就是值传递,将源空间里面的内容复制到目标空间中。
存在缺陷:多个指针可能共用管理一块内存空间,在释放时,导致对一块空间的多次释放,造成内存泄露。
深拷贝:
2. 什么是深拷贝?
在“深拷贝”的情况下,对于对象中动态成员,就不能仅仅简单地赋值了,而应该重新动态分配空间。
深拷贝与浅拷贝:
3.浅拷贝与深拷贝的不同之处:
用深动形象的语言来说,如果把拷贝看作下面这幅图,那么,浅拷贝只能拷走美人鱼的头,水下部分它将无法操作,而深拷贝 不仅可以拷走头,还可以操作水下隐含的部分。
好了,说到这里,咱们可能对深浅拷贝或多或少的有了一些认识,那么,下面,我们将实现一个String 类,来更加具体权威的解释深浅构造函数。
4.完成String类-普通版(浅拷贝)
#include<iostream>using namespace std;class String{ public : String(const char*pStr="")//构造函数 { if(NULL==pStr) { _pStr=new char[1]; *_pStr='\0'; } else { _pStr=new char[strlen(pStr)+1]; strcpy(_pStr,pStr); } } String(const String& s)//拷贝构造函数 :_pStr(s._pStr) { //s2已经释放,但s1不知道,会对空间再次释放 } String& operator=(const String& s)//赋值运算符重载 { if(this!=&s) { _pStr=s._pStr;//内存泄露 } return *this; }~String ()//析构函数{ if(_pStr) { delete[] _pStr; _pStr=NULL; }}private: char* _pStr;};void FunTest(){ String s1("hello"); //String s2(s1); String s3; s3=s1;}int main (){ FunTest(); /*String s1("hello"); String s2(s1); String s3(NULL); String s4(s1); s3=s4; s3=s1;*/ return 0;}该例证明了浅拷贝会存在多个对象共用同一块空间,在调用析构函数销毁空间时,会出现对一块空间多次释放的情况,导致内存崩溃
5. 完成String类深拷贝—简洁版
#include<iostream>using namespace std;class String{public ://构造函数************************************************ String(const char*pStr="") { if(NULL==pStr) { _pStr=new char[1]; *_pStr='\0'; } else { _pStr=new char[strlen(pStr)+1]; strcpy(_pStr,pStr); } }//拷贝构造函数******************************************** String(const String& s) :_pStr(NULL)//选择最佳 { //_pStr=new char[1];//第二选择 String strTemp(s._pStr ); swap(_pStr,strTemp._pStr); } //赋值 运算符重载******************************************//方式一: String& operator=(const String& s) { if(this!=&s) { String strTemp(s._pStr); //String strTemp(s) ; swap(_pStr,strTemp._pStr) ; } return *this; }//方式二://String& operator=(const String& s)// {// String strTemp(s) ;// swap(_pStr,strTemp._pStr) ;//return *this;//} //方式三: //String& operator=( String s) //{ // swap(_pStr,s._pStr ); // return *this; //}//三种赋值运算符重载解决方案, 第一种方式为最优方案//析构函数*****************************************~String (){ if(_pStr) { delete[] _pStr; _pStr=NULL; }}private: char* _pStr; int _count;};void FunTest(){ String s1("Hello"); String s2(s1); s2=s1;}int main (){ FunTest(); /*String s1("hello"); String s2(s1); String s3(NULL); String s4(s1); s3=s4; s3=s1;*/ return 0;}
6.引用计数:
A.什么是引用计数?
在开辟空间时,为了记录该空间有多少对象在共用它,也就是说有多少指针指向它,采用再开辟一个空间的方式,记录该空间被指向的次数,这种方式被称为引用计数。
B.用引用计数实现String时,引用计数可以普通的成员变量?为什么?
解析:引用计数不可以为普通的成员变量,因为一旦出了作用域,该空间被销毁,达不到想要的效果
C.用引用计数实现String时,引用计数可以类的静态成员变量吗? 为什么?
解析:类的静态成员变量,但在需要另外开辟空间时,采用这种方式就
#include<iostream>using namespace std;class String{public : //构造函数 String(const char* pStr="") { if(NULL==pStr) { _pStr=new char[1]; *_pStr='\0'; } else { _pStr=new char[strlen(pStr)+1]; strcpy(_pStr,pStr); } _count=1; }String (const String& s) :_pStr(s._pStr) { _count++; }~String (){ if(_pStr&&(0==--*_count)) { delete[] _pStr; _pStr=NULL; }}private: char* _pStr; static int _count; };int String:: _count=0;void FunTest(){ String s1("hello"); String s2(s1); String s3;}int main(){ FunTest(); return 0;}
为了在释放的时候,防止忘记释放引用计数所开辟的空间,所以尽量采用new [ ]
的方式来开辟空间,与delete[ ]搭配使用。
7.完成引用计数版本的String类—该引用计数也属于浅拷贝
//************引用计数*******************************#include<iostream>using namespace std;class String{public : //构造函数 String(const char* pStr="") :_pCount(new int (1)) { if(NULL==pStr) { _pStr=new char[1]; *_pStr='\0'; } else { _pStr=new char[strlen(pStr)+1]; strcpy(_pStr,pStr); } }String (const String& s) :_pStr(s._pStr) ,_pCount(s._pCount) { ++(*_pCount); }String& operator=(const String& s){ if(_pStr!=s._pStr)//被赋值的对象与当前对象不是同一块空间 { if(_pStr&&0==--*_pCount) { delete [] _pStr; delete _pCount; } _pStr =s._pStr; _pCount=s._pCount; ++_pCount; } return *this;}~String (){ if(_pStr&&(0==--*_pCount)) { delete[] _pStr; _pStr=NULL; delete _pCount; _pCount=NULL; }}private: char* _pStr; int* _pCount;};void FunTest(){ String s1("hello"); String s2(s1); String s3;}int main(){ FunTest(); return 0;}
7. 完成COW(写时拷贝版的String)
(COW不是奶牛)
//****写时拷贝:如果要朝当前对象写东西,最好使用这种方式*********//单线程不会有问题#include<iostream>using namespace std;class String{public : //构造函数 String(const char* pStr="") { if(NULL==pStr) { _pStr=new char[1+4]; _pStr+=4; *_pStr='\0'; } else { _pStr=new char[strlen(pStr)+1+4]; _pStr+=4; strcpy(_pStr,pStr); } GetRaf()=1; }String (const String& s) :_pStr(s._pStr) { GetRaf()++; }String& operator=(const String& s){ if(_pStr!=s._pStr)//被赋值的对象与当前对象不是同一块空间 { Release(); _pStr =s._pStr; ++GetRaf(); } return *this;}~String (){ Release();}char& operator [] (size_t index){ if(GetRaf()>1)//如果当前空间不止存放的一个对象 { char* pTemp=new char [strlen(_pStr)+1+4]; *(int*)pTemp=1; pTemp+=4; strcpy(pTemp,_pStr); --GetRaf();//一定是在改变指针指向之前 _pStr=pTemp; } return _pStr[index];}const char& operator [](size_t index)const { return _pStr[index];}private: int& GetRaf() { return *((int*)_pStr-1); } void Release() { if(_pStr&&(0==--GetRaf())) { _pStr-=4; delete[] _pStr; _pStr=NULL; } }private: char* _pStr;};void FunTest(){ String s1("hello"); String s2(s1); s2[0]='w';}int main(){ FunTest(); return 0;}
下面,在这里提出两点关于string类来说非常重要点:
一.熟悉库中string类的每个接口,查文档,熟悉库中的string类。
二.调研vs和linux系统下string类的结构,他们是否采用用深拷贝原理实现。
- C++深拷贝与浅拷贝(实现String类)
- 【C++】浅析浅拷贝,深拷贝及写时拷贝(copy_on_write),模拟实现String类。
- 【C++】浅拷贝和深拷贝(String类)
- string类的简单实现(深拷贝,浅拷贝)
- String类的实现(深拷贝or浅拷贝)
- C++——string的深拷贝与浅拷贝
- String类 (浅拷贝/深拷贝/写时拷贝)
- 浅拷贝与深拷贝并实现String
- 【C++】c++String类浅拷贝、深拷贝
- C++【常见面试题】String类的实现,以及深拷贝、浅拷贝问题
- 【C++】浅拷贝和深拷贝以及怎样实现一个简单的string类!!!
- String类的浅拷贝与深拷贝
- String类的拷贝(浅拷贝,深拷贝,写时拷贝)
- String类的浅拷贝,深拷贝
- c++-深拷贝与浅拷贝
- 【c++】浅拷贝与深拷贝
- Objective-C 深拷贝与浅拷贝
- [C++]深拷贝与浅拷贝
- JavaScript数组遍历的几种方式
- 排序
- 模板元编程
- 对caffe2的一些初步体会(草稿)
- PG10 中pg_current_wal_insert_lsn()和pg_walfile_name()的使用
- C++深拷贝与浅拷贝(实现String类)
- echarts世界国家中英文对照
- 饿了么组件库element-ui正则表达式验证表单,后端验证表单
- HDU
- 机器学习概念(1)
- python学习笔记-01
- C#基础知识点
- React Native 集成原生极光JMessage 踩坑
- linux解决xhost: unable to open display ""