解决浅拷贝的几种方式
来源:互联网 发布:粒子群算法工具箱 编辑:程序博客网 时间:2024/06/06 18:27
对库函数的操作的熟练掌握是十分重要的,今天我实现的是一个简洁版本的MyString,包括它的浅拷贝,深拷贝以及引用计数版本的MyString。
一.简单的浅拷贝也就是值拷贝,包括对输入输出运算符的重载;
//值拷贝,存在问题class MyString{ friend ostream& operator<<(ostream& os,MyString& str); //输入输出运算符的重载 friend istream& operator>>(istream& is,MyString& str);public: MyString(const char *str="") //全缺省的构造函数,解决空字符串的问题 :_str(new char[strlen(str)+1]) { strcpy(_str,str); } MyString(const MyString& str) //浅拷贝也就是值拷贝 :_str(str._str) {} MyString& operator=(const MyString& str) { if(this != &str) { _str=str._str; } return *this; } ~MyString() { if(_str) { delete[]_str; } }private: char *_str;};ostream& operator<<(ostream& os,MyString& str){ os<<str._str; return os;}istream& operator>>(istream& is,MyString& str){ is>>str._str; return is;}void TestMyString(){ MyString str("hello str"); MyString strCopy("hello DataCopy"); MyString strTmp(strCopy); //简单的值拷贝 cout<<strTmp<<endl; strTmp=str; cout<<strTmp<<endl; //此时多个指针都指向同一块空间,error};
二.深拷贝
在上面实现的浅拷贝版本的MyString里面是简单的值拷贝,也就是说可能出现多个指针指向同一块空间的情况,此时对这块空间进行析构的时候就会出现问题,因为同一块空间是不允许被析构多次的。
深拷贝解决浅拷贝问题是在拷贝构造或者是赋值运算符重载的时候,也要对指针所指向的空间进行拷贝,此时一个指针就指向一块空间,析构的时候当然不会出现问题啦!
//深拷贝class MyString{ friend ostream& operator<<(ostream& os,MyString& str);public: MyString(const char *str="") :_size(strlen(str)) ,_capacity(strlen(str)+1) ,_str(new char[_capacity]) { cout<<"MyString()"<<endl; strcpy(_str,str); } MyString(const MyString& ps) //深拷贝 :_size(ps._size) ,_capacity(strlen(ps._str)+1) ,_str(new char[_capacity]) { cout<<"MyString(const MyString& ps)"<<endl; strcpy(_str,ps._str); } //MyString& operator=(MyString& ps) //{ // cout<<"MyString& operator=(MyString& ps)"<<endl; // if(this != &ps) // { // delete[]_str; // _str=new char[strlen(ps._str)+1]; // strcpy(_str,ps._str); // } // return *this; //} //现代的方法,利用临时变量出作用域就会被析构的特性 MyString& operator=(MyString ps) { cout<<"MyString& operator=(MyString& ps)"<<endl; std::swap(_str,ps._str); std::swap(_size,ps._size); std::swap(_capacity,ps._capacity); return *this; } ~MyString() { cout<<"~MyString()"<<endl; if(_str) { delete[]_str; _str=NULL; _size=0; _capacity=0; } }private: int _size; //字符的个数 int _capacity; //字符的容量 char *_str;};ostream& operator<<(ostream& os,MyString& str){ os<<str._str; return os;}void TestMyString(){ MyString str("hello str"); MyString strCopy("hello DeapthCopy"); MyString strTmp(strCopy); cout<<strTmp<<endl; strTmp=str; cout<<strCopy<<endl;};
三.引用计数-写时拷贝
在深拷贝解决浅拷贝问题的时候是拷贝了指针所指向的空间的内容,这样的方法有些浪费空间了。如果我们可以预留出一块空间专门用来表示所指向空间的指针的个数,如果有新的指针指向这块空间这个计数器就+1,有的读者可能就会产生疑问了啊这不是和浅拷贝的思路一样了吗?析构的时候不会出现问题吗?确实,但是在引用计数的析构函数里面只要该指针不是最后一个指向该空间的指针,我们就让其计数器减1,否则才析构该空间;
第一版的引用计数的方式解决浅拷贝的问题:
//引用计数版的MyStringclass MyString{ friend ostream& operator<<(ostream& os,MyString& str);public: MyString(const char *str="") :_str(new char[strlen(str)+1+4]) //多分配4个整数用来存储指向该空间的指针个数 { cout<<"构造"<<endl; _str += 4; GetCount()=1; //初始的计数器的值为1 strcpy(_str,str); } MyString(const MyString& str) :_str(str._str) { cout<<"拷贝构造"<<endl; ++GetCount(); } char& operator[](size_t index) //写时拷贝的方式 { cout<<"operator[]"<<endl; assert(index > 0 && index < strlen(_str)); if(GetCount() > 1) { --GetCount(); char *tmp=new char[strlen(_str)+5]; strcpy(tmp+4,_str); Delete(); _str=tmp+4; GetCount()=1; } return _str[index]; } MyString& operator=(MyString& str) { cout<<"operator="<<endl; if(this != &str) { if(--GetCount() == 0) { Delete(); } ++str.GetCount(); _str=str._str; } return *this; } ~MyString() { cout<<"析构"<<endl; if(--GetCount() == 0) { cout<<"删除"<<endl; Delete(); } }private: int& GetCount() //获取计数器的值 { return *(int *)(_str-4); } void Delete() //删除空间,连同计数器的空间也一起删除 { delete[](_str-4); }private: char *_str;};ostream& operator<<(ostream& os,MyString& str){ os<<str._str; return os;}void TestMyString(){ MyString str("hello str"); MyString strCopy("hello ReCount"); MyString strTmp(str); cout<<strTmp<<endl; strTmp=strCopy; cout<<strTmp<<endl; strTmp[5]='-'; cout<<strTmp<<endl;}
第二版的引用计数的方式解决浅拷贝的问题:
这个版本是重新分配一个整形的空间来存储指向某一空间的指针的个数,而前面那个版本的是直接多开辟4个字节的空间用来存储指向某一空间的指针的个数。
class MyString{ friend ostream& operator<<(ostream& os,MyString str);public: MyString(const char *str="") :_str(new char[strlen(str)+1]) ,_pCount(new int[sizeof(int)]) { cout<<"构造"<<endl; *_pCount=1; strcpy(_str,str); } MyString(const MyString& str) :_str(str._str) ,_pCount(str._pCount) { cout<<"拷贝构造"<<endl; GetCount()++; } MyString& operator=(MyString& str) { if(this != &str) { if(--GetCount() == 0) //如果之前只有一个指针指向该空间 { Delete(); } str.GetCount()++; _str=str._str; _pCount=str._pCount; } return *this; } char& operator[](size_t index) { cout<<"operator[]"<<endl; assert(index > 0 && index < strlen(_str)); if(GetCount() > 1) { --GetCount(); //开辟新的存放数据的空间和存放计数器的空间 char *tmpChar=new char[strlen(_str)+1]; int *tmpInt=new int[sizeof(int)]; strcpy(tmpChar,_str); *tmpInt=*_pCount; Delete(); //删除this所指向的_str和__pCount _str=tmpChar; _pCount=tmpInt; GetCount()=1; } return _str[index]; } ~MyString() { cout<<"析构"<<endl; if(--GetCount() == 0) { cout<<"删除"<<endl; Delete(); } }private: int& GetCount() { return *_pCount; } void Delete() { delete[]_str; delete[]_pCount; }private: char *_str; int *_pCount;};ostream& operator<<(ostream& os,MyString str){ os<<str._str; return os;}void TestMyString(){ MyString str("hello str"); MyString strCopy("hello ReCount"); MyString strTmp(strCopy); cout<<strTmp<<endl; //hello ReCount strTmp=str; cout<<strTmp<<endl; //hello str-->str strTmp strCopy strCopy=str; strTmp[5]='-'; cout<<strTmp<<endl;}
在这里就分享结束了~~~
阅读全文
0 0
- 解决浅拷贝的几种方式
- mysql拷贝表的几种方式
- mysql拷贝表的几种方式
- C# 拷贝数组的几种方式
- mysql拷贝表的几种方式
- MySQL拷贝表的几种方式
- mysql拷贝表的几种方式
- mysql拷贝表的几种方式
- mysql拷贝表的几种方式
- mysql拷贝表的几种方式
- mysql拷贝表的几种方式
- 字符串拷贝函数strcpy的几种实现思想方式
- 主机与设备之间文件拷贝的几种方式
- Java数组声明与拷贝的几种方式
- Linux下的几种文件拷贝方式效率对比
- mysql 拷贝表(复制表)的几种方式
- 浅谈Python3.6版本的几种拷贝方式
- 解决hibernate懒加载的问题的几种方式
- 排名
- 【css】下拉菜单那些事儿
- 悬镜安全实验室携新产品“云鉴”亮相NSC2017中国网络安全大会
- 【操作系统】处理机调度的层次和调度算法的目标
- 亚军进化史---去深圳之前的大学最后狂欢
- 解决浅拷贝的几种方式
- Zookeeper实例ZkClient API-获取子节点列表
- [java]让人尴尬的一段代码
- IO中同步、异步与阻塞、非阻塞的区别
- MFC异常“所需资源不可用”
- Zookeeper实例ZkClient API-获取节点数据内容
- windows 利用SC 删除与增加 服务
- 设计师经常面对的11种职场问题
- 利用base64函数,对文件进行转码加密