C++ String的实现
2016-04-06 17:53
543 查看
String的实现需要注意的是String的拷贝构造。它的拷贝构造有深拷贝和浅拷贝之分。
我们先来用浅拷贝实现String
![](http://s1.51cto.com/wyfs02/M02/7E/97/wKiom1cE006h6wLEAAAgqxHOc44715.png)
所以在这里我们要采用深拷贝的方式
构造函数和赋值运算符重载
![](http://s2.51cto.com/wyfs02/M00/7E/93/wKioL1cE1Tbx3ARFAACNVhiAVJc549.png)
还有一种方法可以解决这个一块空间会被多次释放的问题,那就是写时拷贝
在第一次构造一个对象的时候,多开辟四个字节当做计数器,用来记录有几个指针指向这块空间。每当用这块空间拷贝构造一个新对象或者把这块空间赋给另外一个对象时,计数器相应增加。那么当调用析构函数时,每次计数器减一,当计数器减到一时,说明只有一个指针指向这块空间,此时再把这块空间delete,就不会出现一块空间多次释放的问题了。
![](http://s3.51cto.com/wyfs02/M02/7E/97/wKiom1cE2V2jqZHuAAAoTAB_f3c973.png)
但是这样做也有一个坏处。就是指向同一块空间的指针,只要改一个指针指向的内容,等于其他的指针指向的内容也跟着改变了。
我们先来用浅拷贝实现String
class String { public: String() { str = new char('A'); } String(char *s) { str = new char[strlen(s) + 1]; if (str != NULL) { strcpy(str, s); } } String(const String& s) { str=s.str; } String& operator=(const String& s) { if (this != &s) { str=s.str; } return *this; } private: char *str; }; void test() { String s1("hello world"); String s2(s1); }当s1,s2出自的作用域时,会自动调用析构函数,此时s1,s2指向同一片内存。所以这块内存会被释放两次,程序会崩溃。
![](http://s1.51cto.com/wyfs02/M02/7E/97/wKiom1cE006h6wLEAAAgqxHOc44715.png)
所以在这里我们要采用深拷贝的方式
构造函数和赋值运算符重载
String(const String& s) { str = new char[strlen(s.str) + 1]; //new出来一块新的空间 if (str) { strcpy(str, s.str); } } String& operator=(const String& s) { if (this != &s) { if (str != NULL) { delete[] str; str = new char[strlen(s.str) + 1]; strcpy(str, s.str); } } return *this; }
![](http://s2.51cto.com/wyfs02/M00/7E/93/wKioL1cE1Tbx3ARFAACNVhiAVJc549.png)
还有一种方法可以解决这个一块空间会被多次释放的问题,那就是写时拷贝
在第一次构造一个对象的时候,多开辟四个字节当做计数器,用来记录有几个指针指向这块空间。每当用这块空间拷贝构造一个新对象或者把这块空间赋给另外一个对象时,计数器相应增加。那么当调用析构函数时,每次计数器减一,当计数器减到一时,说明只有一个指针指向这块空间,此时再把这块空间delete,就不会出现一块空间多次释放的问题了。
class String { public: String(char *str="") :_str(new char[strlen(str)+1+4]) { *((int *)_str) = 1; //将开辟的空间前4个字节强制转换成整型并赋为1 _str = _str + 4; //将_str重新定为真正字符串开始的地方,这样比较方便 strcpy(_str, str); //不用每次向后找 } String(const String& str) { _str = str._str; (*((int *)(_str - 4)))++; } ~String() { if (*_str != NULL) { if (((*((int *)(_str - 4)))--) == 0) //判断计数器是否减到0 { delete[] (_str-4); } } } public: String& operator=(const String& str) { if (this != &str) { if (((*((int *)(_str - 4)))--) == 0) { delete[] (_str-4); } _str = str._str; (*(int *)(str._str - 4))++; return *this; } } char& operator[](int index) { char *tmp = _str; if (((*(int *)(_str - 4))-1) != 0) { (*(int *)(_str - 4))--; _str = new char[strlen(_str) + 5]; (*(int *)(_str + 4)) = 1; _str = _str - 4; strcpy(_str, tmp); } return _str[index]; } private: char *_str; };
![](http://s3.51cto.com/wyfs02/M02/7E/97/wKiom1cE2V2jqZHuAAAoTAB_f3c973.png)
但是这样做也有一个坏处。就是指向同一块空间的指针,只要改一个指针指向的内容,等于其他的指针指向的内容也跟着改变了。
相关文章推荐
- Android之获取手机上的图片和视频缩略图thumbnails
- android string.xml文件中的整型和string型代替
- Android java 与 javascript互访(相互调用)的方法例子
- android上改变listView的选中颜色
- String.intern
- Prototype源码浅析 String部分(二)
- Ruby中的String对象学习笔记
- PostgreSQL ERROR: invalid escape string 解决办法
- C#拷贝文件简单实现方法
- C++中拷贝构造函数的应用详解
- 浅谈C++中的string 类型占几个字节
- 标准C++类string的Copy-On-Write技术
- C++实现string存取二进制数据的方法
- C#中string和StingBuilder内存中的区别实例分析
- PHP STRING 陷阱原理说明
- c#中 String和string的区别介绍
- C#实现的图片、string相互转换类分享
- asp.net String.IsNullOrEmpty 方法
- JavaScript中字符串(string)转json的2种方法
- C#中string用法实例详解