因为c++库中本身就存在string类,我们的模拟实现为了和原因的区分开,这里我定义一个名叫YY的命名空间,接下来我们的模拟实现全部在这个命名空间中进行。

构造函数

调试结果

我们自己实现的构造函数没有问题,s1被成功的初始化为hello world。
析构函数
析构之后,s1中的_str空间释放了,同时也被置成了空指针,_size和_capacity也被改成了0,所以这里的析构函数也是没有任何问题。

拷贝构造
默认拷贝构造
我们先用默认生成的拷贝构造试试看。

这里我们使用默认的拷贝构造出现了错误,这里就涉及到了深拷贝和浅拷贝的知识。
浅拷贝
看下图我们可以知道,s2确实以及被成功的初始化成了hello world,那么为什么会出错了,我们接着往下看。

当我们析构完s2时,可以发现这里s1成了一连串的奇怪字符,而问题也就出在这里。

这个错误就是因为我们使用的默认拷贝构造函数是浅拷贝,浅拷贝是按字节拷贝,拷贝完之后,s2和s1公用同一块空间,而当s2析构之后空间已经被释放,当s1再要去访问时就出错了。

自定义拷贝构造
深拷贝
为了解决浅拷贝发生的问题,所以这里我们自己实现一个深拷贝拷贝构造。

当加上深拷贝之后,我们就发现编译器现在已经不报错了。
深拷贝其实就是自己重新开辟一块空间,然后在这块空间中把内容复制过来。

拷贝构造现代写法

简化版(string类中只有char*str)
这里我们给_str初始化为nullptr的原因是,在调用swap时将nullptr给临时对象tmp,这样tmp在析构销毁时就不会出错,如果没有给_str初始化,那么_str就是一个随机值,给到tmp析构时会出错。

运算符重载
赋值运算符

通过调试我们可以看到s2已经成功赋值给了s1。

现代写法

现代写法(string类中只有char*str)
写法一:

方法二:
很妙

关系运算符重载
operator<

operator==

<=、>、>=、!=
这四个关系运算符重载全部复用上面两个关系运算符重载。

IO流运算符重载
operator<<

operator>>

c_str

这个函数返回的是一个字符串的指针,并且这个数组包含了\0。

size
返回字符串中的字符个数。

operator[]

如果需要检查是否越界我们可以加上assert(pos < _size)。

通过size和[],我们就可以遍历字符串和修改字符串了。


const operator[]
const[],我们使用这个带const的函数,那么就只能查看,不能修改。

使用场景如下,函数f的形参是有const修饰的,所以下面的s[0]会去调用const char& operator[](size_t pos) const这个函数。

迭代器
begin end

用迭代器遍历字符串s1。

用迭代器修改字符串s1。

const迭代器

范围for
范围for在编译时其实被替换成迭代器。

我们将begin修改成Begin,再用范围for就出错了。其实本质上范围for就是用的迭代器。


增容
在string类后增加一个字符

在string类后面增加一个字符串
这里的reserve函数和上面在string类后增加一个字符的reserve函数一样。

reserve
这个函数用于扩容。

resize
调整字符串的大小,将字符串调整为大小为n的字符串,如果n小于_size,那么就把_size的大小改为n,如果n大于_size,那么也把_size调整为n,后面扩大的位置上的字符初始化为ch。

operator+=
+=字符
在字符串后面再尾插一个字符。

+=字符串
在字符串后面再尾插一个字符串。

find
查找一个字符的下标位置,如果没找到则返回npos。

insert
在指定位置插入一个字符。

在指定位置插入一个字符串。

erase
从字符串的pos位置开始,删除len个数的字符。
