[C++]String类的理解
来源:互联网 发布:ip和端口查询 编辑:程序博客网 时间:2024/06/04 14:02
如果不主动编写拷贝构造函数和赋值函数,编译器将以“位拷贝”
的方式自动生成缺省的函数(浅拷贝)。倘若类中含有指针变量,那么这两个缺省的函数就隐含了错误。
以类 String 的两个对象 a,b 为例,假设 a.m_data 的内容为“hello”, b.m_data 的内容为“world”。现将a赋给b,缺省赋值函数的“位拷贝”意味着执行b.m_data= a.m_data。 这将造成三个错误:
1. b.m_data 原有的内存没被释放,造成内存泄露;
2. b.m_data 和 a.m_data 指向同一块内存,a 或 b 任何一方变动都会影响另一方;
3. 在对象被析构时,m_data 被释放了两次。
故需要深拷贝
下面是最容易理解的一种写法:
#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<string.h>using namespace std;class String{ friend ostream& operator<<(ostream &out,String &rhs); friend istream& operator>>(istream &in,String &rhs);public: String(char *str = "") :_pstr(new char[strlen(str)+1]) { strcpy(_pstr,str); } String(const String& s) :_pstr(new char [strlen(s._pstr)+1]) { strcpy(_pstr,s._pstr); } String& operator=(const String& other) //1.检查自赋值 //2.用 delete 释放原有的内存资源 //3.分配新的内存资源,并复制字符串 //4.返回本对象的引用(连等) { if(this != &other) { delete []_pstr; _pstr = new char [strlen(other._pstr)+1]; strcpy(_pstr,other._pstr); } return *this; } ~String() { if(_pstr != NULL) delete []_pstr; } void Debug() { cout<<"string:> "<<_pstr<<endl; cout<<"count:> "<<*_ref<<endl; }private: char *_pstr;};ostream& operator<<(ostream &out,String &rhs){ out<<rhs._pstr; return out;}istream& operator>>(istream &in,String &rhs){ in>>rhs._pstr; return in;}void Fun1(){ String A("aaaaa"); String B(A); String C = B; C = A; cin>>C; cout<<C<<endl;}
下面还有一种简洁的拷贝构造和赋值运算符重载函数:
理解图:
String(const String& s) :_pstr(NULL)//没有指向的指针不能交换里面的内容 { String temp(s._pstr); std::swap(temp._pstr,_pstr); } String& operator=(String rhs)//运算符重载隐含了左值,故不需要赋空 //此时没有将参数赋为const String& other的原因: //将下面实现换成注释也可完成同样功能,但重复了上面的拷贝构造内容,如换成没有注释的形式,则增强代码重用率,在下面进行交换的时候可以重复调用拷贝构造函数,也可达到赋值的目的。 { //String temp(s._pstr); //std::swap(temp._pstr,_pstr); std::swap(_pstr,rhs._pstr); return *this; }//返回值联等 参数会产生临时变量
然而深拷贝并不见的完全好,如果多个对象都存储了相同的内容,那岂不是很浪费空间,于是我们又发明了一个叫做引用计数的东西,就是把相同内容的对象只存储一遍,用一个计数器记下他的数目,每构造一次将其置一,每拷贝构造一次将其加一,每赋值一次将其加一(原来的引用计数减一),但如果将引用计数设为 int _ref 我们每次进行操作都会对其原来的引用计数加一(其实他原来的_ref并不需要改变,甚至在调用赋值重载时还会减一),而将其换为 static int _ref 时,如果我们申请了新的对象
比如: String A; String B(A); String C;
C的出现会将前面A B的引用计数一同改变,所以这也不是什么好办发,然而最好的办法是将引用计数设为指针
int * _ref 这样实现起来解容易多了,也可像库函数STL实现一样将该引用计数放于私有成员字符串的前4个字节,节省了一个私有成员的开销,这种方法与库函数中的new类似,new = 开辟空间(malloc)+调用构造函数(定位new) new operator = 开辟空间。
下面是写时拷贝的实现:
class String{ friend ostream& operator<<(ostream &out,String &rhs); friend istream& operator>>(istream &in,String &rhs);public: String(char *str = "") :_pstr(new char[strlen(str)+1]) ,_ref(new int[1])//申请一个4字节的空间 new int(1) { strcpy(_pstr,str); _ref[0]=1;//里面存放计数 } String(const String &s) :_pstr(NULL) ,_ref(NULL) { _pstr = s._pstr; _ref = s._ref; (*_ref)++; } String& operator=(const String &rhs) { if(rhs._pstr != this->_pstr) { if( --(*_ref) == 0) { delete []_pstr; if(_ref != NULL)//_ref可能为空 delete []_ref; } _pstr = rhs._pstr; _ref = rhs._ref; (*_ref)++;//_red[0]++ } return *this; } ~String() { if(--(*_ref) == 0) { delete []_pstr; if(_ref != NULL) delete []_ref; } } void Debug() { cout<<"string:> "<<_pstr<<endl; cout<<"count:> "<<*_ref<<endl; }private: char *_pstr; int *_ref;};ostream& operator<<(ostream &out,String &rhs){ out<<rhs._pstr; return out;}istream& operator>>(istream &in,String &rhs){ in>>rhs._pstr; return in;}void Fun2(){ String a("qqq"); a.Debug(); String b("dfhdfhd"); //a.Debug(); String c(b); b.Debug(); c = a; a.Debug(); b.Debug(); c.Debug();}
- [C++]String类的理解
- 关于String类的理解
- 关于String类的理解
- String 类的终极理解
- java的String类理解
- String类赋值的理解
- 教你编写STL的string类-01(理解C/C++内存管理)
- 教你编写STL的string类-02(理解C/C++内存管理)
- C++-类的理解
- 对String类的深刻理解
- java中对于String类的理解
- 关于String类的一些理解
- 黑马程序员--对String类的理解
- 本人对String类的一些理解
- Java中String类的一些理解
- JavaSE--06--String类的理解
- String类的使用与理解
- java String类字符串常量的理解
- python 中 #!/usr/bin/env python 与 #!/usr/bin/python的区别
- 学习笔记4/3(使用int问题
- 如何处理空指针警告
- _stdcall与_cdecl区别
- 分布式开发 (负载均衡图解)
- [C++]String类的理解
- Android UI 同步
- Creating a Content Provider
- [iOS开发]屏幕适配二:Xcode7使用Autolayout拖拽布局基础(1)
- Qt Quick无边框窗口
- 【bzoj3165】【HEOI2013】【Segment】【线段树】
- IIS配置—端口映射—外网访问网站
- java开源项目CrapApi接口管理系统
- 枚举 类型转换