自己实现简单的string类
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
⾃⼰实现简单的string类
1.前⾔
最近看了下《C++Primer》,觉得受益匪浅。
不过纸上得来终觉浅,觉知此事须躬⾏。
今天看了类类型,书中简单实现了String类,⾃⼰以前也学过C++,不过说来惭愧,以前都是⽤C来写程序,学的C++基本都忘记了,也说明⾃⼰以前对C++的理解不够深⼊。
基于这些,觉得有必要动⼿来写写C++的⼀些程序了,毕竟C++有很多的功能是C所不具备的。
正好看了课本中String类的简单实现,⽽且string类在
C++中的使⽤频率也很⾼,了解其内部的实现是很有必要的。
所以今天打算写个string类,就当做练⼿吧。
2.String类的设计
写类⾸先是定义类的名字,我们实现的string就叫String吧,以防和标准库中的string类冲突。
其次是类中的内部数据,既然是字符串类,必须要把字符串类所代表的字符串保存起来,所以必须定义⼀个存放这些字符的字符数组或者⼀个指向字符数组的指针。
我是⽤后者实现,因为这样更为的灵活,不会因为提前定义数组的⼤⼩⽽限制了能存放到数组中的字符个数。
为了⽅便,可以定义字符串的长度,当然也可以不定义,可以在字符数组的末尾存放0来表⽰字符串的结束,不过每次得到字符串的长度⽐较⿇烦。
最后是类对外的接⼝,根据我们平时使⽤string类的情况,我们⼀般会⽤到接⼝size(), c_str(),还有就是+,=,>>,<<,+=,[],==等操作符。
根据前⾯的分析,可以得到我们要设计的String类中的成员如下所⽰:
1class String
2 {
3public:
4 String();
5 String(const char *);
6 String(const String &);
7 String(String &&); //新加的move构造函数
8
9 ~String();
10
11 String& operator=(const char *);
12 String& operator=(const String &);
13
14bool operator==(const char *);
15bool operator==(const String &);
16
17char &operator[](int);
18
19 String operator+(const char *);
20 String operator+(const char);
21 String operator+(const String &);
22
23 String &operator +=(const char *);
24 String &operator +=(const char);
25 String &operator +=(const String &);
26
27int size(){return _size;}
28char *c_str(){return _string;}
29
30 friend istream &operator>>(istream &cin, String &str);
31private:
32int _size;
33char *_string;
34 };
3.String类的主要实现
我们先来说⼀下构造函数的实现。
由于我想实现的String类中存放字符的数组⼤⼩是可以根据实际需要存放字符的个数来动态调整的(这样对于存放字符的个数就没有限制,除⾮内存不够⽤了),所以必须根据实际存放的字符个数来动态的申请内存空间,代码可以⽤如下的⽅式实现:
1 String::String(const char *str)
2 {
3if (!str)
4 {
5 _size = 0;
6 _string = NULL;
7 }
8else
9 {
10 _size = strlen(str);
11 _string = new char[_size + 1];
12 strcpy(_string, str);
13 _string[_size] = 0;
14 }
15 }
View Code
由于构造函数动态申请了内存,所以必须定义析构函数来释放我们申请的内存空间。
1 String::~String()
2 {
3if (_string)
4 delete _string;
5
6 cout << "~String() call" << endl;
7 }
View Code
同样,由于我们每个String类都会动态的申请内存空间,所以必须定义拷贝构造函数,否则默认的拷贝构造函数会导致多个String对象共享相同内存空间的问题(深拷贝与浅拷贝的的问题)。
代码和前⾯的构造函数差不多。
1 String::String(const String &str)
2 {
3if (!str._size)
4 {
5 _size = 0;
6 _string = NULL;
7 }
8else
9 {
10 _size = str._size;
11 _string = new char[_size + 1];
12 strcpy(_string, str._string);
13 _string[_size] = 0;
14 }
15
16 }
View Code
其它的函数就是对+,=,+=操作符的重载,原理都是⼀样的,需要重新分配内存空间来适应新的字符串个数的需求,不同的是+的返回类型不需要使⽤引⽤。
具体代码如下(⽤+=实现的):
String String::operator+(const String &str)
{
assert(_string && str._string);
String str_temp(*this);
str_temp += str;
return std::move(str_temp);
}
View Code
由于函数+会返回对象,编译器会默认产⽣⼀个临时的对象,这个对象完全是返回对象的拷贝,⽽返回对象马上就会析构掉,所以如果我们能把返回对象中的字符数组地址拷贝到临时对象中,那么临时对象就可以不⽤再申请内存了,这样可以提供效率。
所以这⾥我实现了⼀个move函数(的提醒):
String::String(String && str)
{
_size = str._size;
_string = str._string;
str._string = NULL;
}
View Code
还有我们经常⽤的是直接对string类进⾏输出,所以我们必须重载<<,>>操作符,由于输⼊需要⽤到String类中的私有成员变量
_string,所以应该把输⼊函数设置为String类的友元函数。
⽽输出可以通过调⽤c_str函数获得字符数组地址来输出。
istream &operator>>(istream &cin, String &str)
{
const int limit_string_size = 4096;
str._string = new char[limit_string_size];
cin >> setw(limit_string_size) >> str._string;
str._size = strlen(str._string);
return cin;
}
ostream &operator<<(ostream &cout, String &str)
{
return cout << str.c_str();
}
View Code
编写这个String类遇到了两个⼩的问题,⼀个就是在写+=操作符重载函数的时候,忘记在函数的前⾯写String::,导致编译器总是包各种莫名的错误,什么参数不对,不能访问内部成员,这个问题完全是⾃⼰粗⼼导致的。
另⼀个问题就是写+操作符重载函数,开始写+函数的时候直接修改了当前的String对象,后来测试发现这是有问题的,因为+应该重新返回⼀个新的String对象,这个新String对象是当前String对象与传⼊参数的字符串的和,这个问题完全是⾃⼰没有想清楚+与+=的区别,+=才是要修改当前String对象的。