1、传统的实现string类的方法
优点:程序简单易懂
缺点:
1)在实现其拷贝构造和赋值的操作时多次的调用new动态的开辟空间,因此也需要多次的通过delete来释放空间。如果处理不当还容易造成内存泄漏。
2)程序的一致性比较差
#include <iostream> #include <string> using namespace std; class String { public: // 构造函数 String(char *str = "") :_str(new char [strlen(str)+1]) { if (_str != NULL) { strcpy(_str, str); } } //拷贝构造 String(const String& str) :_str(new char[strlen(str._str) + 1]) { if (_str != NULL) { strcpy(_str, str._str); } } //赋值 String& operator = (const String& str) { if (this != &str)//自赋值问题 { delete[]_str;//释放之前开辟的空间 _str = new char[strlen(str._str) + 1];//开辟新的空间 strcpy(_str, str._str);//拷贝 } return *this; } //析构函数 ~String() { delete [] _str; } private: char * _str; };
2、现代的实现string类的方法写实
拷贝构造实现思路:
a)创建一个临时对象,并通过cconst String._str来构造。
b)构造完成之后将临时对象的_str和this._str进行交换进而使得对象的内容交换来完成拷贝构造
赋值实现思路:
通过参数的值传递来构造对象,并将对象的内容交换
优点:
1)程序的一致性比较好
2)只是在构造函数中出现了开辟空间的new,只在析构函数中出现了delete结构清晰,不容易造成内存泄漏
class String { public: // 构造函数 String(char *str = "") :_str(new char [strlen(str)+1]) { if (_str != NULL) { strcpy(_str, str); } } //拷贝构造 String(const String& str) :_str(NULL)//防止其指向一份不合法的空间 { String tmp(str._str); swap(_str, tmp._str); } //赋值 String& operator =(String str) { swap(_str, str._str); return *this; } //析构函数 ~String() { delete [] _str; } private: char * _str; };
3、写实函数提高string类的效率
在以上的两种string类的实现方法中,都存在一个效率的问题。有的时候字符串比较长,而且对象建立之后也不一定更改。但是以上两种方法无论何时都需要构建,其效率比较低而且会带来内存的浪费。
我们可以通过让所有指向相同字符串内容的对象指向同一份空间,通过计数器来记录对象的个数,避免析构出现错误。
所以可以通过在开辟对象的时候多开辟4个字节的空间,来存放计数器。如果修改的时候再开辟空间。
#define _CRT_SECURE_NO_WARNINGS #include<iostream> #include <string> using namespace std; class String { public: //构造函数 String(char *str="") :_Pstr(FindStartRef(new char[strlen(str)+1+sizeof(int)])) { *Count(_Pstr) = 1;//计数器 strcpy(_Pstr, str);//将字符串拷贝 } //拷贝构造函数 //如果不更改对象的内容则让多个对象的字符指针指向同一份空间 String(const String& s) { _Pstr = s._Pstr; (*Count(_Pstr))++;//计数器加1 } //赋值语句 //如果不更改对象的内容则让多个对象的字符指针指向同一份空间 String& operator =(const String& s) { if (this != &s)//避免自赋值 { if (--*(Count(_Pstr)) == 0) { delete[]FindDel(_Pstr); }//只有一个对象使用的一份空间则释放 else { _Pstr = s._Pstr; (*Count(_Pstr))++; } } return *this; } //析构函数 ~String() { if (--*(Count(_Pstr)) == 0) { delete[]FindDel(_Pstr); } } public: //找到开辟空间时的存放字符串的首地址 char * FindStartRef(char* str) { return (str + 4); } //找到释放空间时的首地址 char * FindDel(char* del) { return (del - 4); } //找到计数器的首地址 int *Count(char* str) { return (int *)(str - 4); } public: //修改写实对象的内容函数 char & operator[](int index) { if (--*(Count(_Pstr)) != 0) { char * tmp = _Pstr; _Pstr = FindStartRef(new char[strlen(_Pstr) + 1 + sizeof(int)]); *Count(_Pstr) = 1;//计数器置1 strcpy(_Pstr, tmp); }//如果该对象和其他对象公用一份空间 else { //单独使用一份空间可以随意更改 } return _Pstr[index]; } private: char * _Pstr; };
原文地址:http://10788311.blog.51cto.com/10778311/1749275