c++|C++(从零开始,万字模拟实现string(超详细))

前言:因为模拟实现string中途修修改改了很多代码,所以建议大家先复制总代码在自己的vs下,然后再来一步一步看
目录
第一步:将"hello world"赋值给自定义的string
第二步:打印出hello world在屏幕上
第三步:用下标可以修改字符串的任意字符
第四步:通过下标遍历string的每个字符
第五步:浅拷贝和深拷贝字符串
第六步:=运算符的重载
第七步:实现增删查改——修改基础private
第八步:增删查改——添加字符和字符串(resize和reserve,push_back和append)
第十步:三种遍历方式
第十一步:增删查改——指定插入和指定删除(insert和erase的实现)
第十二步:实现增删查改——查找find函数实现
总代码:
string.h:
test.cpp:

------------------------开始------------------------
为了模拟string,我们需要自己命名一个新的空间,我这里是’bit’,以方便和库中的string作区分
然后创建两个,一个是头文件’string.h’,另一个是.cpp文件"test.cpp"
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片


第一步:将"hello world"赋值给自定义的string
我这里自己定义了一个空间名字为”bit”,然后定义了一个String类。对于我们定义的新字符串_str,
我们需要定义出他的大小,因为原本的字符串中有’\0’,所以在进行字符串拷贝的时候,我们需要在原本的空间大小上+1,然后利用strcpy进行拷贝。
最后再加入一个析构函数

namespace bit { class String { public: String(const char* str)//因为有’\0’,所以需要在原有的基础上+1个空间 :_str(new char[strlen(str) + 1])//比如'hello world',实际上是'hello world\0' { strcpy(_str, str); //拷贝字符串 } ~String()//析构函数,将资源还给内存 { if (_str) { delete[] _str; } } private: char* _str; //自定义一个自己的新指针,指向一个只有'\0'的字符串空间 }; }

将上述代码放入string.h中,然后我们在test.cpp调用以下代码进行测试
#include #include"string.h" using namespace std; //上面是头文件,下面才是代码 void test_string1() { bit::String s1("hello world"); } int main() { test_string1(); return 0; }

在监视窗口中我们可以看到,我们已经将hello world赋予了,我们自己创建的string的s1中
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

第二步:打印出hello world在屏幕上 因为我们还没有重载流插入和流提取的运算符,所以如果我们要输出hello world,这里可以使用一个函数c_str.
在string.h的public中加入这段代码
const char* c_str()const { return _str; }

然后就可以在test.app中使用了
void test_string1() { bit::String s1("hello world"); cout << s1.c_str() << endl; //加入这句话 }

然后我们在屏幕上就可以看到这段话
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

注:如果不了解c_str的小伙伴可以看看我的这篇文章
C++:什么是c_str_幻荼的博客-CSDN博客


第三步:用下标可以修改字符串的任意字符 对于修改字符,我们就需要对[]进行运算符重载
在string.h的public放入
char& operator[](size_t pos) { assert(pos < strlen(_str)); //记得在前面加头文件 return _str[pos]; }

然后在test.cpp中修改字符
void test_string1() { bit::String s1("hello world"); cout << s1.c_str() << endl; s1[0] = 'X'; //加入这句话 cout << s1.c_str() << endl; //加入这句话 }

c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

我们可以看到hello world的第一个字符h被替换成了X。
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

备注:如果对运算符重载有问题的小伙伴可以观看我的这篇文章C++:运算符重载简介_幻荼的博客-CSDN博客

第四步:通过下标遍历string的每个字符
一共是有三种方式:
1.下标+[]
2.迭代器
3. 范围for
因为是从零开始模拟,我们这里先讲解第一种,下标+[]。
我们首先在string.h的public中加入size函数,以便求得下标的整体范围.
size_t size() { return strlen(_str); }

有了这个函数,我们就能再test中遍历每个字符了
void test_string1() { bit::String s1("hello world"); cout << s1.c_str() << endl; s1[0] = 'X'; cout << s1.c_str() << endl; for (size_t i = 0; i < s1.size(); i++)//加入这段话 { cout << s1[i] << ' '; } cout << endl; //加入这段话 }

会有以下结果
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

我们可以看到我们就遍历出了每个字符。
备注:如果对strlen不了解的小伙伴可以看我写的这篇文章
strlen简介,和sizeof的区别_幻荼的博客-CSDN博客
第五步:浅拷贝和深拷贝字符串 我们以前使用string的时候,喜欢把一个字符串直接赋值给另一个字符串,即
string s1("123456"); string s2(s1);

那么如何模拟实现呢?
我们先看看直接在test.cpp中试试能不能做到
void test_string2()//这里为了演示深浅拷贝,我重新创立了一个test_string2,前面是1,请将这段代码全部打到test.cpp中 { bit::String s1("hello world"); bit::String s2(s1); cout << s1.c_str() << endl; cout << s2.c_str() << endl; }

然后就会出现这个结果:
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

c++|C++(从零开始,万字模拟实现string(超详细))
文章图片


结果就是,拷贝是拷贝过去了,但是编译器会报错,这是为什么呢?
这个就和我们的深浅拷贝有关了
简单来说就是p2通过浅拷贝,和p1指向了同一个空间
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

由于栈区的规则是先进后出,当执行完拷贝构造函数的时候,我们程序就会先执行p2的析构函数,导致释放堆区开辟的数据,然后这个已经释放的内存又要再被p1释放,这样就会报错
就是说同一块内存数据被释放了两次,这是编译器所不允许的
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片


所以我们需要进行深拷贝。
深拷贝简单来说就是开一个和p1一样大的空间,然后将p1的数据存放到新空间,然后自己指向新空间
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

这样解析的时候,就各自解析各自的了。
为了实现深拷贝,我们只需要加入这段代码:
String(const String& s)这个隐藏了一个this指针,实际上是s2(s1) :_str(new char[strlen(s._str) + 1])//开辟(new)一个新空间和s1一样大,来存放数据,+1是因为有’\0’ { strcpy(_str, s._str); //拷贝数据 }


备注:不了解深浅拷贝的小伙伴可以看看我的这篇文章
C++:浅拷贝和深拷贝简析_幻荼的博客-CSDN博客
第六步:=运算符的重载 为了实现’+=’'+''-'各种运算符的重载,我们首先来实现最简单的’=’符号,即赋值。
我们设定一个场景
void test_string3()//这里是运算符重载,所以重新开了一个test3 { bit::String s1("hello world"); bit::String s2(s1); bit::String s3 ("ni hao"); s1 = s3; //通过’=’,将s3赋值给s1 cout << s1.c_str() << endl; cout << s2.c_str() << endl; cout << s3.c_str() << endl; }

有些同学可能会说,直接用strcpy将s3复制给s1不就行了吗?
但是对于赋值,我们有三种情况
s1空间 s1空间=s3空间
s1空间>s3空间
如果我们直接用strcpy复制,s1空间 列如:
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

如果这么多个1通过strcpy,s1的空间是肯定放不下的,就会造成越界访问。
那么如果s1空间>s3空间是不是可以了呢?
答案是:可以,但是会造成浪费
列如:
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

如果我S1开了一个1000个字节的空间,结果只用来存放s3这样一个字符’1’,那么是不是会造成很严重的浪费呢?
所以说为了避免越界访问和内存浪费,我们就要使得s1的空间大小和s3一模一样。
所以我们进行三步
第一步:删除s1原有内存
第二步:创建一个和s3一样大的内存
第三步:复制数据
这段代码加入到string.h的public中
String& operator=(const String& s)//这里有隐藏的this指针s3(s1) { if(this!=&s)//防止有人自己给自己赋值,列如s1=s1,这样会导致乱码 { delete[]_str; //删除s1空间 _str = new char[strlen(s._str) + 1]; //创建和s3一样大的空间 strcpy(_str, s._str); //复制数据 return *this; //返回数据 } }

然后运行结果如下
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

就可以看到s1被s3赋值了
第七步:实现增删查改——修改基础private 通过前面六步,实际上一个最基础的string已经完成了,为了丰富我们的string,我们就要实现增删查改的功能。
为了实现这些功能,一个_str肯定已经不够了,我们需要size和capacity,即容量和已存储数据的大小
private: char* _str; //自定义一个自己的新字符串接受 size_t _size; //容量(新加入) size_t _capacity; //目前大小(新加入) };

然后加入新的capacity函数
size_t capacity()const { return _capacity; }


同时我们一系列的函数就需要调整
构造函数
String(const char* str="")//这里全缺省,加入一个空字符串,空字符串中有'\0',防止默认构造出错 :_size(strlen(str))//新加入 ,_capacity(_size)//新加入 { _str = new char[_capacity + 1]; //将开空间调到这里 strcpy(_str, str); }

因为编译器是根据private的顺序进行定义,我们这里_str的初始化使用了capacity,而capacity在privat的最后面,所以为了方便,我们就直接把他在里面初始化了。

然后是 拷贝
String(const String& s) :_size(strlen(s._str)) ,_capacity(_size) { _str = new char[_capacity + 1]; strcpy(_str, s._str); }

最后是’=’的重载
String& operator=(const String& s) { if (this != &s) { delete[]_str; _str = new char[s._capacity + 1]; strcpy(_str, s._str); _size = s._size; _capacity = s._capacity; } return *this; }


这些修改实际上就是增加了我们新加入的size和capacity,然后把原先用strlen求有效字符串长度用capacity替换。(capacity是有效字符串个数,不包括'\0')
第八步:增删查改——添加字符和字符串(resize和reserve,push_back和append) push_back(添加字符)append(添加字符串)
push_back:
添加字符实际上很简单,只需要考虑扩容,如果容量够直接在末尾添加一个字符,再补一个'\0'即可
void push_back(char ch) { if (_size == _capacity)//查看是否容量已满 { char* tmp = new char[_capacity * 2 + 1]; //new一个新的大空间,+1是因为有'\0' strcpy(tmp, _str); //内容复制给新空间 delete[]_str; //删除旧空间 _str = tmp; //将旧空间指向新空间 _capacity *= 2; //'\0'不是有效字符所以*2就可以了 } _str[_size] = ch; //尾插 ++_size; _str[_size] = '\0'; //最后补一个'\0' }

如下图所示:
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片


然后我们就可以测试一下
void test_string5()//这里是5了 { bit::String s1("hello world"); s1.push_back('1'); s1.push_back('X'); cout << s1.c_str() << endl; }

c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

可以看到1和X就被尾插了。
然后接下来是append:
append和push_back几乎一模一样,都是扩容+尾插,却别在于append的扩容不太好直接扩二倍。
假如
string s("123"); s.append("12345678910");

如果我们只是单纯的扩容2倍,那么可以看到s*2的capacity=6,也不够10个。
所以在这里我们需要计算一共有多少个字符,再根据字符数量来扩容
void append(const char* str) { size_t len = _size + strlen(str); //原本字符数+需要添加的字符数 if (len > _capacity) { reserve(len); //扩容 } strcpy(_str + _size, str); //拷贝数值 _size = len; //拷贝空间 }

小伙伴们可能发现了, 我们这里的扩容运用了一个函数reserve(),而没有像之前push_back一样写一串,这是因为我把扩容,重新用reserve包装了一下
其实和上面扩容一模一样
void reserve(size_t n) { if (n > _capacity)//如果需要扩容 { char* tmp = new char[n + 1]; //创建一个和n一样大的空间防止浪费和越界访问 strcpy(tmp, _str); //复制数据 delete[]_str; //删除旧空间 _str = tmp; //指向新空间 _capacity = n; //把n给capacity } }

然后我们也可以吧reserve给push_back使用一下
void push_back(char ch) { if (_size == _capacity) { reserve(reserve(_capacity == 0 ? 4 : _capacity * 2); ); //这里扩容二倍刚好合适,因为是只插入单个字符 } _str[_size] = ch; ++_size; _str[_size] = '\0'; }

在一些特殊的情况下,我们也会使用resize对数据进行插入和初始化。
列如:
string s1("123456"); s1.resize(10,'9')

这样打印出来的结果是"1234569999".
这里就模拟实现一下resize
void resize(size_t n,char ch='\0') { if (n < _size)//n _capacity)//capacity不够进行扩容 { reserve(n); } for (size_t i = _size; i < n; i++)//对空余进行初始化操作 { _str[i] = ch; } _size = n; _str[_size] = '\0'; } }


至于为什么用reserve,以及什么是resize,不明白的小伙伴可以看我这篇文章
c++:reserve和resize简介和区别_幻荼的博客-CSDN博客
但是————我们平时都不这么用
我们平时插入一个字符串或者字符直接用"+="
例如
string s1("123"); s1+='X'; s1+="686";

【c++|C++(从零开始,万字模拟实现string(超详细))】s1就成为了"123X686";
所以说,这里我们需要对+=进行运算符重载
因为我们已经实现了单个字符的插入和字符串的插入,所以套过去就好
String& operator+=(const char* str)//字符串的插入,记得隐藏的this指针 { append(str); return *this; } String& operator+=(char ch)//字符的插入,记得隐藏的this指针 { push_back(ch); return *this; }

然后我们就可以在test.cpp中测试看看
void test_string5()//这里是用来测试插入字符和字符串的test5 { bit::String s1("hello world"); s1.push_back('1'); s1.push_back('X'); s1.append("www"); s1 += 'y'; s1 += "abc"; cout << s1.c_str() << endl; }

结果如下
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

第九步:各类运算符重载
有不懂运算符的小伙伴,我在前面已经放了链接,可以自行查看,这里因为太简单所以直接贴上代码
bool operator<(const String& s1, const String& s2) { return strcmp(s1.c_str(), s2.c_str()) < 0; } bool operator==(const String& s1, const String& s2) { return strcmp(s1.c_str(), s2.c_str()) == 0; } bool operator<=(const String& s1, const String& s2) { return s1(const String& s1, const String& s2) { return !(s1<=s2); } bool operator>=(const String& s1, const String& s2) { return !(s1 < s2); } bool operator!=(const String& s1, const String& s2) { return !(s1 == s2); }

如果不知道什么是strcmp的小伙伴可以看我的这篇文章
c:strcmp函数()比较两个字符串是否相等_幻荼的博客-CSDN博客_如何比较两个字符串内容是否一样
第十步:三种遍历方式这一段我自己了解的还不是很深刻,只能先讲解我知道的部分,等后面学习了再来补充吧...
我们已经在最开始利用了下标+[]的方式遍历了每个字符
void test_string6()//这里是专门用来测试三种遍历的test6 { bit::String s1("hello world"); for (size_t i; i < s1.size(); i++) { cout << s1[i] << " "; } cout << endl; }

这里我们使用第二种方式迭代器
对于迭代器,我们首先需要begin和end两个自定义函数
typedef char* iterator; iterator begin()//指向首地址 { return _str; } iterator end()//指向末尾元素的下一个元素 { return _str + _size; }

然后就可以和指针一样遍历整个字符串
bit::String::iterator it = s1.begin(); //将字符串第一个字符给it while (it != s1.end())//it不为最后一个字符,就继续循环 { cout << *it << " "; it++; } cout << endl;

实际上就是这么个情况
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

第三种方式范围for(不会,等会面学了再来补充)
for (auto& ch : s1)//实际上底层被替换成了迭代器,这里ch实际上是s1的拷贝 { //ch-=1; 大家可以试着把这段话加上看看,因为这里是引用传参所以可以改变,如果只是auto ch,那么这段代码就没有作用 cout << ch << " "; } cout << endl;

第十一步:增删查改——指定插入和指定删除(insert和erase的实现) 不明白这两个函数是什么的小伙伴可以看看我的这篇文章
c++:insert函数和erase函数的简介_幻荼的博客-CSDN博客
我们刚才已经实现了push_back,和append,以及+=,那么我们算是把尾部的增加给搞定了,但是很多情况下,我们是需要在数据的中间进行增删查改的,所以就需要我们实现指定位置的插入和删除。
insert(指定位置的插入):
对于指定位置的插入,我们一般是插入字符和字符串两种情况。
我们先来实现插入字符
String& insert(size_t pos, char ch) { assert(pos <= _size); if (_size == _capacity)//扩容 { reserve(_capacity == 0 ? 4 : _capacity * 2); } size_t end = _size+1; while (end > pos) { _str[end] = _str[end-1]; --end; } _str[pos] = ch; _size++; return *this; }

实际上是这么个情况,end指向字符串结尾'\0'的下一个位置,然后依次挪动数据,等到pos和end相等的时候,先判断是否扩容,如果不用扩容就直接在pos这个位置插入这个数据,然后size++
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

接下来是字符串:
String& insert(size_t pos,const char* str) { assert(pos <= _size); size_t len = strlen(str); if (len == 0) { return* this; } if (_size + len >= _capacity)//扩容 { reserve(_size + len); } size_t end = _size + len; while (end >pos+len-1 ) { _str[end] = _str[end-len]; --end; } strncpy(_str + pos, str, len); _size += len; return *this; }

实际上是这么个情况:假如我要插入"XXX",的字符在S[0]处,那么我的end就是目前的size+"XXX"字符串的长度,只要当我们pos+”XXX”字符串的长度-1(有"\0")=end,那么我们就停止,然后在pos处用strncpy()函数插入这个字符串
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

好,我们下面来进行调试
void test_string7()//这里是用来调试insert和erase的test7 { bit::String s("hello world"); s.insert(1, 'x'); s.insert(0, "XXX"); cout << s.c_str() << endl; }

结果如下:
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

下面我们来实现指定位置删除erase:
String& earse(size_t pos, size_t len) { assert(pos < _size); if (len == npos || pos + len >= _size)//pos+len>=size { _str[pos] = '\0'; _size = pos; } else//pos+len

意思如下:
假如,我有一个字符串hello world,我从W(pos)向后删除len个字符,有三种情况
pos+len pos+len=size
pos+len>size
一旦我的pos+len>=size,我们就可以直接在s[pos]处赋值一个'\0',然后把size变为pos,因为字符串遇到'\0'就终止
如果pos+len 就是w=l,然后o=d,然后r='\0',然后此时=size,赋值结束。然后把size-=len,就完成了。
c++|C++(从零开始,万字模拟实现string(超详细))
文章图片

第十二步:实现增删查改——查找find函数实现 这一步很简单,没有什么需要讲解的
size_t find(char ch, size_t pos = 0) { for (; pos < _size; pos++)//遍历字符串 { if (_str[pos] == ch)//如果等于就返回该字符 { return pos; } } return npos; } size_t find(const char* str, size_t pos = 0) { const char* p = strstr(_str + pos, str); //用strstr函数进行字符串查找 if (p == nullptr) { return npos; } else { return p - _str; } }

总代码: string.h:
#pragma once #include #include using namespace std; namespace bit { class String { public: typedef char* iterator; typedef char* const const_iterator; const_iterator begin()const { return _str; } const_iterator end()const { return _str + _size; } iterator begin() { return _str; } iterator end() { return _str + _size; } String() :_size(0) ,_capacity(0) { _str = new char[1]; _str[0] = '\0'; } String(const char* str)// :_size(strlen(str)) ,_capacity(_size) { _str = new char[_capacity + 1]; strcpy(_str, str); } String(const String& s) :_size(strlen(s._str)) ,_capacity(_size) { _str = new char[_capacity + 1]; strcpy(_str, s._str); } ~String()//析构函数,将资源还给内存 { if (_str) { delete[] _str; _str = nullptr; _size = _capacity = 0; } } String& operator=(const String& s) { if (this != &s) { delete[]_str; _str = new char[s._capacity + 1]; strcpy(_str, s._str); _size = s._size; _capacity = s._capacity; } return *this; } const char* c_str()const { return _str; } char& operator[](size_t pos) { assert(pos < strlen(_str)); return _str[pos]; } const char& operator[](size_t pos)const { assert(pos < strlen(_str)); return _str[pos]; } void push_back(char ch) { /*if (_size == _capacity) { reserve(_capacity == 0 ? 4 : _capacity * 2); } _str[_size] = ch; ++_size; _str[_size] = '\0'; */ insert(_size, ch); } void resize(size_t n,char ch='\0') { if (n < _size) { _size = n; _str[_size] = '\0'; } else { if (n > _capacity) { reserve(n); } for (size_t i = _size; i < n; i++) { _str[i] = ch; } _size = n; _str[_size] = '\0'; } } String& operator+=(const char* str) { append(str); return *this; } String& operator+=(char ch) { push_back(ch); return *this; } void reserve(size_t n) { if (n > _capacity) { char* tmp = new char[n + 1]; strcpy(tmp, _str); delete[]_str; _str = tmp; _capacity = n; } } void append(const char* str) { /*size_t len = _size + strlen(str); if (len > _capacity) { reserve(len); } strcpy(_str + _size, str); _size = len; */ insert(_size, str); } size_t size()const { return strlen(_str); } size_t capacity()const { return _capacity; } String& insert(size_t pos, char ch) { assert(pos <= _size); if (_size == _capacity) { reserve(_capacity == 0 ? 4 : _capacity * 2); } size_t end = _size+1; while (end > pos) { _str[end] = _str[end-1]; --end; } _str[pos] = ch; _size++; return *this; } String& insert(size_t pos,const char* str) { assert(pos <= _size); size_t len = strlen(str); if (len == 0) { return* this; } if (_size + len >= _capacity) { reserve(_size + len); } size_t end = _size + len; while (end >pos+len-1 ) { _str[end] = _str[end-len]; --end; } strncpy(_str + pos, str, len); _size += len; return *this; } String& earse(size_t pos, size_t len) { assert(pos < _size); if (len == npos || pos + len >= _size) { _str[pos] = '\0'; _size = pos; } else { size_t begin = pos + len; while (begin <= _size) { _str[begin - len] = _str[begin]; begin++; } _size -= len; } return *this; } size_t find(char ch, size_t pos = 0) { for (; pos < _size; pos++) { if (_str[pos] == ch) { return pos; } } return npos; } size_t find(const char* str, size_t pos = 0) { const char* p = strstr(_str + pos, str); if (p == nullptr) { return npos; } else { return p - _str; } } private: char* _str; //自定义一个自己的新指针,指向一个只有'\0'的字符串空间 size_t _size; size_t _capacity; const static size_t npos; }; const size_t String::npos = -1; ostream& operator<<(ostream& out, const String& s) { for (auto ch : s) { out << ch; } return out; } istream& operator>>(istream& in, String& s) { char ch; ch = in.get(); while (ch != ' ' && ch != '\n') { s += ch; ch = in.get(); } return in; } bool operator<(const String& s1, const String& s2) { return strcmp(s1.c_str(), s2.c_str()) < 0; } bool operator==(const String& s1, const String& s2) { return strcmp(s1.c_str(), s2.c_str()) == 0; } bool operator<=(const String& s1, const String& s2) { return s1(const String& s1, const String& s2) { return !(s1<=s2); } bool operator>=(const String& s1, const String& s2) { return !(s1 < s2); } bool operator!=(const String& s1, const String& s2) { return !(s1 == s2); } }

test.cpp:
#define _CRT_SECURE_NO_WARNINGS 1 using namespace std; #include"string.h" void test_string1() { bit::String s1("hello world"); cout << s1.c_str() << endl; s1[0] = 'X'; cout << s1.c_str() << endl; for (size_t i = 0; i < s1.size(); i++) { cout << s1[i] << ' '; } cout << endl; } void test_string2() { bit::String s1("hello world"); bit::String s2(s1); cout << s1.c_str() << endl; cout << s2.c_str() << endl; } void test_string3() { bit::String s1("hello world"); bit::String s2(s1); bit::String s3("ni hao"); s1 = s3; cout << s1.c_str() << endl; cout << s2.c_str() << endl; cout << s3.c_str() << endl; } void test_string4() { bit::String s1("hello world"); cout << s1.c_str() << endl; } void test_string5() { bit::String s1("hello world"); s1.push_back('1'); s1.push_back('X'); s1.append("www"); s1 += 'y'; s1 += "abc"; cout << s1.c_str() << endl; } void test_string6()//这里是专门用来测试三种遍历的test6 { //1.下标+[] bit::String s1("hello world"); for (size_t i=0; i < s1.size(); i++) { cout << s1[i] << " "; } cout << endl; //2.迭代器 bit::String::iterator it = s1.begin(); while (it != s1.end()) { cout << *it << " "; it++; } cout << endl; //3.范围for for (auto ch : s1) { cout << ch << " "; } cout << endl; }void test_string7()//这里是用来调试insert和erase的test7 { bit::String s("hello world"); s.insert(1, 'x'); s.insert(0, "XXX"); cout << s.c_str() << endl; } void test_string9() { bit::String s("hello world"); cout << s << endl; } int main() { try { test_string9(); } catch (const exception& e) { cout << e.what() << endl; } return 0; }

--------------------------完结----------------------------

    推荐阅读