注意点:初始化列表的顺序不能改变,因为变量是按照 初始化列表 的顺序来进行初始化的。
之前在string类(二)中提到简单的功能,这期是在这些功能之上,基础代码:
cpp
//传统写法的string类
#include <iostream>
#include <string>
#include <assert.h>
#include <stdlib.h>
using namespace std;
class String
{
public:
String(const char* str = "") //默认构造函数,没有传参就是空字符串
{
//构造String类对象时,如果传递nullptr指针,可以认为程序非法
if (nullptr == str)
{
assert(false);
return;
}
_str = new char[strlen(str) + 1];//开辟对应的空间,加一是给\0保留位置
strcpy(_str, str); //_str复制str
}
String(const String& s) //计算 strlen(s._str) + 1得到需要分配的内存大小
:_str(new char[strlen(s._str) + 1]) //使用 new运算符在堆上分配相应大小的字符数组
{ //new表达式返回指向分配内存的指针
strcpy(_str, s._str); //这个指针值直接用于初始化 _str成员变量
}
//传统写法
String& operator=(const String& s) //赋值运算符重载 str1 = str2; -> str1.operator=(str2)
{
if (this != &s) //检查自我赋值,this指向调用该成员函数的对象(不是指参数)
{
char* pStr = new char[strlen(s._str) + 1];
strcpy(pStr, s._str); //strcpy函数将源字符串 s._str的内容复制到新分配的内存 pStr中
delete[] _str;
_str = pStr;
}
return *this;
}
//现代写法
String& operator=(String s)
{
swap(_str, s._str);
return *this;
}
~String() //析构函数,不为空就释放空间
{
if (_str)
{
delete[] _str;
_str = nullptr;
}
}
private:
char* _str;
};
一些简单功能的实现:
cpp
// 返回c语言形式的字符串
const char* c_str() const
{
return _str;
}
// 返回有效的字符个数
size_t Size() const
{
return _size;
}
访问string的下标:
operator [] 需要注意两个接口,一个接口是可读可写的函数,也就是针对的是非const 对象;另一个是只可读的 const 对象:【至于为什么有两个接口?在string类(二)讲过】
可读可写(非const对象):
cpp
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
只可读(const对象): //const 成员函数 (重点介绍)
cpp
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
上述的只可读接口函数 当中,这是一个const成员函数,函数声明末尾的 const
关键字修饰的是隐含的 this
指针, 实际上const 将this指针从String* const 变为**const String* const,**修。因为const对象只能调用const成员函数.
普通版本:
cpp
const String s("hello"); // const 对象
char c = s[0]; // 错误!可读可写版本的 operator[]
提供的const版本:
cpp
const String s("hello"); // const 对象
char c = s[0]; // 正确!调用 const 版本的 operator[]
从String* const 变为**const String* const,**这两种指针类型虽然看起来相似,但有着根本性的区别,顺带复习const。
String* const
:const pointer to String(指向 String 的常量指针)-
const String* const
:const pointer to const String(指向 const String 的常量指针)
详细对比:
- String* const(指向非常量 String 的常量指针,const只修饰ptr指针)
cpp
String str1("Hello");
String str2("World");
String* const ptr = &str1;
// ptr = &str2; // 错误!不能改变指针的指向(因为ptr是常量)
// 通过 ptr 访问的特性:
ptr->append("!"); // 正确!可以修改所指对象的内容
cout << ptr->c_str(); // 正确!可以读取所指对象的内容
指针++不能改变朝向++ ,但是可以改变++指针指向的内容++。
- const String* const(指向常量 String 的常量指针,string也被const修饰)
cpp
const String str1("Hello");
String str2("World");
const String* const ptr = &str1;
// ptr 本身的特性:
// ptr = &str2; // 错误!不能改变指针的指向
// 通过 ptr 访问的特性:
// ptr->append("!"); // 错误!不能修改所指对象的内容
cout << ptr->c_str(); // 正确!可以读取所指对象的内容
指针的指向和内容++都不可被修改++。
简单复习:
在 this 指针上下文中的意义 :
普通成员函数中的
this
cppclass String { public: void modify() { // this 的类型是 String* const // 可以修改成员变量:this->_str = new_value; } };
const 成员函数中的
this
cppclass String { public: void inspect() const { // this 的类型是 const String* const // 不能修改成员变量:this->_str = new_value; // 错误! } };
增
对于字符串的增,实现两个函数,一个是 push_back() 一个是 append() ,push_back() 是尾差一个字符 ,append() 是尾差一个字符串。
当然,增,可能会有一个扩容的问题:对于 push_back() 我们可以直接用 _capacity 的 2 倍的形式扩容;append() 尾差的是字符串,我们不敢直接 扩容2倍。所以,我们这里使用 strlen(str) 计算要插入的字符串的有效字符 + _size 原本字符串数组当中的有效字符作为 扩容的条件和 扩容的大小。
在官方的string类当中,有一个reserve() 扩容函数 ,在这这里我们就直接实现这个函数,那么在上述的 push_back() 一个是 append() 函数中我们就使用这个函数来进行扩容:
实现reserve() :
cpp
// 扩容函数
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
strcpy(tmp, _str);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
扩容之后就可以实现上述两个函数了:
cpp
// 增
void push_back(char ch)
{
// 如果有效字符个数超过了 容量
if (_size >= _capacity)
{
// 2倍扩容
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
void append(const char* str)
{
size_t len = strlen(str);
if (len + _size > _capacity)
{
// 扩容
reserve(len + _size);
}
strcpy(_str + _size, str);
_size += len;
}
当然上述两个函数都不是我们最常用的,最常用的是 operator += 这个运算符重载函数,当然这个函数的底层和上述差不多,只不过在使用的时候更加方便:
他同样有两个接口,一个是 尾插字符,一个是尾差字符串:
cpp
// += 尾差字符
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
对于插入,还有就是在 指定位置(pos)位置插入字符或字符串(insert())函数的实现:
cpp
// 指定位置插入一个或多个字符
void insert(size_t pos, size_t n, char ch)
{
assert(pos <= _size);
if (_size + n >= _capacity)
{
// 扩容
reserve(_size + n);
}
//往后挪动数据
size_t end = _size;
while (pos <= end && end != npos)
{
_str[end + n] = _str[end];
--end;
}
// 覆盖值
for (size_t i = 0; i < n; i++)
{
_str[pos + i] = ch;
}
_size += n;
}
cpp
// 指定位置插入一个字符串
void insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size + len >= _capacity)
{
// 扩容
reserve(_size + len);
}
//往后挪动数据
size_t end = _size;
while (pos <= end && end != npos)
{
_str[end + len] = _str[end];
--end;
}
// 覆盖值
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = *(str + i);
}
_size += len;
}
在实现的时候遇到的问题,如下图:

end是int类型的,pos是size_t类型的,按照上述的调试,本次循环应该退出循环,但是实际上是进入了循环;
其原因是因为,在c语言的语法当中规定,在一个运算符的两边,如果左右两边的操作数的类型不相同,就会发生整形提升,通常是小类型像大类型发生转换,比如如果是 int类型和 double类型,那么int类型会转换为 double类型。
所以,看似上述end到了-1,按道理应该退出循环,但是int类型的 end 发生的整形提升,提升到了 size_t 无符号整形,所以,了解类型的值域的循环的小伙伴就知道,此时end就不会是-1,从无符号整形的视角来看,是全1,就是整形的最大值。自然就不会跳出循环。
解决上述问题的方式有很多,可以强制类型转换:

或者像官方string当中一样设置一个 npoe :

如上设置之后,就可以在while循环当中多增加一个条件,当 end 走到 -1 的之后就停止:

删:
erase函数,从pos位置删除一个或多个字符:
cpp
// 从pos位置删除一个或多个字符
void erase(size_t pos, size_t len = npos)
{
assert(pos <= _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t end = pos + len;
while (end <= _size)
{
_str[pos++] = _str[end++];
}
_size -= len;
}
}
clear()函数,删除字符串当中所有的有效字符:
cpp
// 清除所有有效字符
void clear()
{
_str[0] = '\0';
_size = 0;
}
查
find()函数,在pos位置查找一个字符:
cpp
// 从pos位置往后查找一个字符
size_t find(char ch, size_t pos = 0)
{
for (size_t i = pos; i <= _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
find()函数,从pos位置开始查找一个字符串:
cpp
// 从pos位置开始查找一个字符串
size_t find(const char* str, size_t pos = 0)
{
assert(pos < _size);
const char* ptr = strstr(_str, str);
if (ptr)
{
return ptr - _str;
}
else
{
return npos;
}
}
substr()函数,从字符串数组的pos位置开始取出len个字符大小的字符串:
cpp
// 从pos位置开始,从字符串数组当中取出len个字符的字符串,返回string类
string substr(size_t pos, size_t len = npos)
{
assert(pos < _size);
size_t n = len;
if (len == npos || len + pos >= _size)
{
n = _size - pos;
}
string tmp;
tmp.reserve(n);
for (int i = pos; i < pos + n; i++)
{
tmp += _str[i];
}
return tmp;
}
resize的实现:
resize可以扩容也可以缩容,会初始化,默认 '\0'。
cpp
// resize 删除或扩容添加函数
void resize(size_t n, char ch = '\0')
{
if (n < _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
// 先检测 扩容
reserve(n);
for (int i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
}
operator<<(流插入的实现)
基本实现方式:
cpp
// 正确的流插入操作符实现
std::ostream& operator<<(std::ostream& out, const std::string& str)
{
// 方式一:使用下标运算符访问
for (size_t i = 0; i < str.size(); i++)
{
out << str[i];
}
// 方式二:使用范围for循环(更现代、简洁)
for (auto ch : str)
{
out << ch;
}
return out;
}
此处的 打印和 使用 str 来打印,两者其实有差别的,
str 打印的是一个字符串,是一个内置类型,打印这个字符串是以 '\0' 作为终止符的,而流输入是插入多少就打印多少,所以遇见 '\0' 是不会停止的,会出现问题。
所以推荐使用memcpy();
cpp
// 使用 memcpy 的正确方式
std::string::string(const std::string& other)
{
_size = other._size;
_capacity = other._capacity;
_str = new char[_capacity + 1];
// 使用 memcpy 而不是 strcpy
memcpy(_str, other._str, _size + 1); // 拷贝所有字节,包括中间的 \0
}
operator>>(流提取的实现)
有问题的实现:
cpp
// 有问题的初始实现
std::istream& operator>>(std::istream& in, std::string& str)
{
char ch;
in >> ch; // 这里使用 >> 会跳过空格和换行
while (ch != ' ' && ch != '\n')
{
str += ch;
in >> ch; // 同样会跳过空格和换行
}
return in;
}
in >> ch
会跳过空白字符(空格、制表符、换行等),因此循环永远不会遇到空格或换行,导致无法检测到终止条件,死循环。
而且,我们使用 流提取来对string类对象当中的字符串数组进行写入数据的话,我们希望的是覆盖,而不是像 operator+= 函数一样尾插,那我们可以考虑先用 clear() 函数对字符串进行清除。
解决方法,在 istream 这个流提取当中有一个接口 get(),它默认每次只读取一个字符,不管这个字符是 空格 还是 换行。
代码如下:
cpp
istream& operator>> (istream& in, string& str)
{
str.clear();
char ch = in.get();
while (ch != ' ' && ch != '\n')
{
str += ch;
ch = in.get();
}
return in;
}
这算是基础的正确实现。
改进点:
- 使用
in.get()
而不是in >> ch
,因为前者会读取所有字符(包括空白字符) - 先调用
str.clear()
清空字符串,确保是覆盖而非追加
基础实现虽然正确,但性能上有缺陷:
- 每次添加一个字符可能导致频繁的内存重新分配
- 对于长字符串,性能较差
性能优化实现:
开辟一个临时数组,这个数组的大小可以自己规定,规定大小为 128 个字符;相当于是把我们输入的字符串,以一组127个有效字符,分割成很多组,当一组的字符填满之后,在对str对象当中的字符串数组进行填写,这样就避免了 在 operator+= 当中很多次的扩容操作。
还需要优化的是,上述我们的实现的流提取,遇到换行或者空格就会停止,那么如果我们在输入有效字符之前,有空格或者换行,那么就会直接停止,但是在官方的string当中的流提取是会把前面的空格和换行清除的,所以我们这加一个循环,把有效字符之前的空格和换行给删除了。
最终代码实现:
cpp
istream& operator>> (istream& in, string& str)
{
str.clear();
char ch = in.get();
while (ch == ' ' || ch == '\0')
{
ch = in.get();
}
char Buff[128];
int i = 0;
while (ch != ' ' && ch != '\n')
{
Buff[i++] = ch;
if (i == 127)
{
Buff[i] = '\0';
str += Buff;
// 重置i
i = 0;
}
ch = in.get();
}
// 如果此时 i 不是0,说明Buff 当中还有字符没有 += 完
if (i != 0)
{
Buff[i] = '\0';
str += Buff;
}
return in;
}
比较大小operator<
可以直接使用 C 当中strcmp() 这个库函数来实现,但是还是会出现和上述一样的问题,如果有效字符当中有 '\0' ,那么就会出现问题,所以我们应该使用 memcmp()。
但是,memcmp()还是有问题,如下两种情况,两个字符串字符个数不相等,还是比较麻烦的:
所以还是要自己来实现,其实自己实现也不难:
- 两个字符串一起走,如果当前哪一个字符串的字符的ascll值大,那么他就大,反之;如果当前两个字符串的字符相等,那么就继续往后走。
- 之后到最后,有两种情况,一种是两个字符串字符相等,字符个数也相等,那么这两个字符串就相等;另一种就是上述说的两种情况,短的字符串走完了,长的字符串没有走完,那么长的字符串就是大的那一个字符串;
代码实现:
cpp
int operator< (const string& str)
{
//return strcmp(_str, str._str) < 0;
size_t i1 = 0;
size_t i2 = 0;
while (i1 < _size && i2 < str._size)
{
if (_str[i1] < str._str[i2])
{
return true;
}
else if (_str[i1] > str._str[i2])
{
return false;
}
else
{
++i1;
++i2;
}
}
/*if (i1 == _size && i2 != str._size)
{
return true;
}
else
{
return false;
}*/
// 或者
// return _size < str._size;
// 或者
return i1 == _size && i2 != str._size;
}
复用 memcmp()函数实现的代码如下:
cpp
int operator< (const string& str)
{
int Mybool = memcmp(_str, str._str, _size < str._size ? _size : str._size);
return Mybool == 0 ? _size < str._size : Mybool < 0;
}
operator== <= > >= !=
写好一个之后,后面的就简单了,可以直接复用:
cpp
bool operator== (const string& str) const
{
return _size == str._size &&
memcmp(_str, str._str, _size) == 0;
}
bool operator<= (const string& str) const
{
return *this < str || *this == str;
}
bool operator> (const string& str) const
{
return !(*this <= str);
}
bool operator>= (const string& str) const
{
return !(*this < str);
}
bool operator!= (const string& str) const
{
return !(*this == s);
}
operator=
文献参考:C++-string类的模拟实现-CSDN博客 //来源博主:chihiro
赋值拷贝有两种,一种是浅拷贝,一种是深拷贝:
来看下面这个代码:
cpp
string& operator= (const string& str)
{
if (this != &str)
{
string tmp(str);
std::swap(_str, tmp._str);
std::swap(_size, tmp._size);
std::swap(_capacity, tmp._capacity);
}
return *this;
}
如上,新创建一个和 str 一样的对象(调用了拷贝构造函数)-tmp,然后把tmp和 this对象的数组指针和 所以成员交换一下,然后就可以实现深拷贝了,这是一个很妙的写法,看下图:

如上所示,tmp新开辟了一块空间,s1想着,反正你tmp 的生命周期 就在这个函数当中,出了这个函数,tmp就需要调用析构函数,释放空间,那么s1就把他的空间给tmp,tmp就把新开辟的,和s3赋值好的空间给s1,然后tmp在最后释放空间的时候就释放的是s1原本的空间,相当于是tmp为s1把原空间给释放掉了。
注意:在赋值操作符重载函数当中不能像如下一样写:
cpp
string& operator= (const string& str)
{
string tmp(str);
std::swap(tmp , *this);
return *this;
}
像上述一样写会造成递归死循环。
这时候,swap()这个函数在两个参数都是对象的时候,它其中调用的就是 operator= 赋值操作符重载函数,如下所示:

那像上述代码就会在swap 和 operator= 之间来回跳 ,造成递归式的死循环。
所以在像上述一样使用swap 的时候,还是需要自己实现swap()函数:
就如上述string类的swap如下所示实现:
cpp
class string
{
`````````````
void swap(string& str)
{
std::swap(_str, str._str);
std::swap(_size, str._size);
std::swap(_capacity, str._capacity);
}
````````````
};
根据上述实现的swap()函数,上述的 operator= 函数可以如下优化:
cpp
string& operator= (string& str)
{
swap(str);
return *this;
}
上述相当于把两个string对象的 所有成员 都给交换了。
cpp
string& operator= (string str)
{
swap(str);
return *this;
}
而上述才是和之前一样,是传值拷贝,需要创建临时对象,也就是这个str就是局部变量,局部变量出了这个函数作用域就结束了生命周期,相当于是 str 帮s1 (*this)把s1原本的空间释放掉了。
基于上述方法,对拷贝构造函数的优化
基于上述的优化,我们可以优化拷贝构造函数,我们也可以在拷贝构造函数使用和上述一样的方式来,让编译器来帮我们释放空间:
cpp
string(const string& s)
{
string tmp(s._str);
// 让tmp来帮s对象释放掉原来的空间
swap(tmp);
}
上述是对拷贝构造函数的优化,但是上述还是有问题:
上述代码在一般的编译器当中就会报错,编译器对于类当中的内置类型,一般来说是不会自动做初始化的,如果你在一些环境当中看见初始化的,都属于是这个编译器的优化,但是不是所以的编译器都会这样做,所以我们不敢依靠编译器来自动初始化,我们需要手动初始化。
上述的 this 对象没有手动初始化,对于其中的内置类型(_size 和 _capacity )来说,是随机值,那么tmp交换的是一个(_size 和 _capacity )是随机值的对象,在这个函数结束之后tmp调用 析构函数 delete 释放空间的时候就会出现问题。
所以我们应该这样写:
cpp
string(const string& s)
:_str(nullptr)
,_size(0)
,_capacity(0)
{
string tmp(s._str);
// 让tmp来帮s对象释放掉原来的空间
swap(tmp);
}
需要注意的是,上述是直接用s._str 来构造的tmp对象,那么对于 下述情况就会出现问题:
cpp
"hello\0worle"
对于上述这个字符串,他只能拷贝 hello。所以如果是有上述情况使用的话,建议使用之前使用的拷贝构造函数来实现。
string类的完整代码:
cpp
#pragma once
#include<assert.h>
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
using namespace std;
namespace string_begin
{
class string
{
public:
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
iterator begin() const
{
return _str;
}
iterator end() const
{
return _str + size();
}
// 构造函数
//string()
// :_str(new char[1]),
// _size(0),
// _capacity(0)
//{
// _str[0] = '\0';
//}
//string(const char* str)
// : _str(new char[strlen(str) + 1]),
// _size(strlen(str)),
// _capacity(strlen(str))
//{
// strcpy(_str, str);
//}
//string(const char* str = '\0') // 错误写法
//string(const char* str = nullptr) // 错误写法
//string(const char* str = "\0") // 可以但是没有必要下面更好
string(const char* str = "")
: _str(new char[strlen(str) + 1]),
_size(strlen(str)),
_capacity(strlen(str))
{
memcpy(_str, str, _size + 1);
}
// 拷贝构造函数(深拷贝)
//string(const string& s)
//{
// _str = new char[s._capacity + 1];
// //strcpy(_str, s._str);
// memcpy(_str, s._str, s._size + 1);
// _capacity = s._capacity;
// _size = s._size;
//}
string(const string& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
string tmp(s._str);
// 让tmp来帮s对象释放掉原来的空间
swap(tmp);
}
// 析构函数
~string()
{
delete _str;
_str = nullptr;
_size = _capacity = 0;
}
// 返回c语言形式的字符串
const char* c_str() const
{
return _str;
}
// 返回有效的字符个数
size_t size() const
{
return _size;
}
// 下标+引用返回的运算符重载函数
// 要提供两个版本,一个是非const对象的,一个是const对象的
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos) const
{
assert(pos < _size);
return _str[pos];
}
// 扩容函数
void reserve(size_t n)
{
if (n > _capacity)
{
char* tmp = new char[n + 1];
memcpy(tmp, _str, _size + 1);
delete[] _str;
_str = tmp;
_capacity = n;
}
}
// 增
void push_back(char ch)
{
// 如果有效字符个数超过了 容量
if (_size >= _capacity)
{
// 2倍扩容
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size] = ch;
_size++;
_str[_size] = '\0';
}
void append(const char* str)
{
size_t len = strlen(str);
if (len + _size > _capacity)
{
// 扩容
reserve(len + _size);
}
//strcpy(_str + _size, str);
memcpy(_str + _size, str, len + 1);
_size += len;
}
// += 尾插字符
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
// 指定位置插入一个或多个字符
void insert(size_t pos, size_t n, char ch)
{
assert(pos <= _size);
if (_size + n >= _capacity)
{
// 扩容
reserve(_size + n);
}
//往后挪动数据
size_t end = _size;
while (pos <= end && end != npos)
{
_str[end + n] = _str[end];
--end;
}
// 覆盖值
for (size_t i = 0; i < n; i++)
{
_str[pos + i] = ch;
}
_size += n;
}
// 指定位置插入一个字符串
void insert(size_t pos, const char* str)
{
assert(pos <= _size);
size_t len = strlen(str);
if (_size + len >= _capacity)
{
// 扩容
reserve(_size + len);
}
//往后挪动数据
size_t end = _size;
while (pos <= end && end != npos)
{
_str[end + len] = _str[end];
--end;
}
// 覆盖值
for (size_t i = 0; i < len; i++)
{
_str[pos + i] = *(str + i);
}
_size += len;
}
// 从pos位置删除一个或多个字符
void erase(size_t pos, size_t len = npos)
{
assert(pos <= _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
size_t end = pos + len;
while (end <= _size)
{
_str[pos++] = _str[end++];
}
_size -= len;
}
}
// 从pos位置往后查找一个字符
size_t find(char ch, size_t pos = 0)
{
assert(pos <= _size);
for (size_t i = pos; i <= _size; i++)
{
if (_str[i] == ch)
{
return i;
}
}
return npos;
}
// 从pos位置开始查找一个字符串
size_t find(const char* str, size_t pos = 0)
{
assert(pos <= _size);
const char* ptr = strstr(_str, str);
if (ptr)
{
return ptr - _str;
}
else
{
return npos;
}
}
// 从pos位置开始,从字符串数组当中取出len个字符的字符串,返回string类
string substr(size_t pos, size_t len = npos)
{
assert(pos < _size);
size_t n = len;
if (len == npos || len + pos >= _size)
{
n = _size - pos;
}
string tmp;
tmp.reserve(n);
for (int i = pos; i < pos + n; i++)
{
tmp += _str[i];
}
return tmp;
}
// resize 删除或扩容添加函数
void resize(size_t n, char ch = '\0')
{
if (n < _size)
{
_size = n;
_str[_size] = '\0';
}
else
{
// 先检测 扩容
reserve(n);
for (int i = _size; i < n; i++)
{
_str[i] = ch;
}
_size = n;
_str[_size] = '\0';
}
}
// 清除所有有效字符
void clear()
{
_str[0] = '\0';
_size = 0;
}
// 比较string大小
//int operator< (const string& str) const
//{
// //return strcmp(_str, str._str) < 0;
// size_t i1 = 0;
// size_t i2 = 0;
// while (i1 < _size && i2 < str._size)
// {
// if (_str[i1] < str._str[i2])
// {
// return true;
// }
// else if (_str[i1] > str._str[i2])
// {
// return false;
// }
// else
// {
// ++i1;
// ++i2;
// }
// }
// /*if (i1 == _size && i2 != str._size)
// {
// return true;
// }
// else
// {
// return false;
// }*/
// // 或者
//
// // return _size < str._size;
// // 或者
// return i1 == _size && i2 != str._size;
//}
// < 当中复用 memcmp
bool operator< (const string& str) const
{
int Mybool = memcmp(_str, str._str, _size < str._size ? _size : str._size);
return Mybool == 0 ? _size < str._size : Mybool < 0;
}
bool operator== (const string& str) const
{
return _size == str._size &&
memcmp(_str, str._str, _size) == 0;
}
bool operator<= (const string& str) const
{
return *this < str || *this == str;
}
bool operator> (const string& str) const
{
return !(*this <= str);
}
bool operator>= (const string& str) const
{
return !(*this < str);
}
bool operator!= (const string& str) const
{
return !(*this == str);
}
void swap(string& str)
{
std::swap(_str, str._str);
std::swap(_size, str._size);
std::swap(_capacity, str._capacity);
}
// 复制操作符重载函数
//string& operator= (string& str)
//{
// if (this != &str)
// {
// char* tmp = new char[str._capacity + 1];
// memcpy(tmp, str._str , str._size + 1);
// delete[] _str;
// _str = tmp;
// _size = str._size;
// _capacity = str._capacity;
// }
// return *this;
//}
//string& operator= (const string& str)
//{
// if (this != &str)
// {
// string tmp(str);
// //std::swap(_str, tmp._str);
// //std::swap(_size, tmp._size);
// //std::swap(_capacity, tmp._capacity);
// swap(tmp);
// }
// return *this;
//}
string& operator= (string str)
{
swap(str);
return *this;
}
protected:
char* _str;
size_t _size;
size_t _capacity;
size_t static npos;
};
size_t string::npos = -1;
ostream& operator<< (ostream& out, string& str)
{
//// 方式一
//for (int i = 0; i < str.size(); i++)
//{
// out << str[i];
//}
// 方式二
for (auto ch : str)
{
out << ch;
}
return out;
}
istream& operator>> (istream& in, string& str)
{
str.clear();
char ch = in.get();
while (ch == ' ' || ch == '\0')
{
ch = in.get();
}
char Buff[128];
int i = 0;
while (ch != ' ' && ch != '\n')
{
Buff[i++] = ch;
if (i == 127)
{
Buff[i] = '\0';
str += Buff;
// 重置i
i = 0;
}
ch = in.get();
}
// 如果此时 i 不是0,说明Buff 当中还有字符没有 += 完
if (i != 0)
{
Buff[i] = '\0';
str += Buff;
}
return in;
}
}