本文主要介绍string类和该类常用的接口,并根据接口功能对其进行模拟实现。
目录
一、string类的常用接口说明
1. string类对象的常见构造
2.string类对象的容量操作
3.string类对象的访问及遍历操作
4.string类对象的修改操作
5. string类非成员函数
二、string的模拟实现
1.默认成员函数和成员变量
浅拷贝与深拷贝
2.迭代器相关函数的实现
3.字符串大小与容量相关函数的实现
4.字符串修改操作相关函数的实现
5.字符串访问相关函数的实现
6.字符串比较相关函数的实现
7.流插入和流提取的实现
8.完整代码
在使用string类时,必须包含#include
(下面我们只介绍最常用的接口)
(文档链接)
函数名称 | 功能说明 |
---|---|
string(); | 构造空的string类对象,即空字符串 |
string (const string& str); | 用C-string来构造string类对象 |
string (size_t n, char c); | string类对象中包含n个字符c |
string (const string& str); | 拷贝构造函数 |
演示:
string(); //构造空的string类对象,即空字符串
string (const string& str);//用C-string来构造string类对象
string (size_t n, char c);//string类对象中包含n个字符c
string(const string&s) //拷贝构造函数
void StringTest()
{
string s1; // 构造空的string类对象s1
string s2("hello world"); // 用C格式字符串构造string类对象s2
string s3(6,'x');//有6个'x'字符的字符串构造string类对象s3
string s4(s2); // 拷贝构造s4
}
函数名称 | 功能说明 |
---|---|
size | 返回字符串有效字符长度 |
length | 返回字符串有效字符长度 |
capacity | 返回空间总大小 |
empty | 检测字符串释放为空串,是返回true,否则返回false |
clear | 清空有效字符 |
reserve | 为字符串预留空间 |
resize | 将有效字符的个数该成n个,多出的空间用字符c填充 |
1.size 和 length:返回字符串有效字符长度
size_t size() const;
size_t length() const;
int main()
{
string s1("hello world");
cout << s1.size() << endl;//11
cout << s1.length() << endl;//11
return 0;
}
size()与length()方法底层实现原理完全相同,引入size()的原因是为了与其他容器的接口保持一 致,一般情况下基本都是用size()。
2.capacity:返回空间总大小
size_t capacity() const;
int main()
{
string s1("hello world");
string s2("C++");
cout << s1.capacity() << endl;//15
cout << s2.capacity() << endl;//15
return 0;
}
返回当前对象中该字符串所分配的存储空间的大小。
3.empty:检测字符串释放为空串,是返回true,否则返回false
bool empty() const;
int main()
{
string s1("hello world");
string s2;
if (s1.empty())
cout << "s1 is empty" << endl;
if (s2.empty())
cout << "s2 is empty" << endl;
return 0;
}
输出:s2 is empty
4.clear:清空有效字符
void clear();
int main()
{
string s("hello world");
cout << s.size() << endl;// 11
cout << s.length() << endl;// 11
cout << s.capacity() << endl;// 15
cout << s << endl;// hello world
s.clear();
cout << s.size() << endl;// 0
cout << s.capacity() << endl;// 15
return 0;
}
该函数将s中的字符串清空,注意清空时只是将size清0,不改变底层空间的大小
5.reserve:为字符串预留空间
void reserve (size_type n = 0);
int main()
{
string s("hello world");
cout << s.size() << endl;// 11
cout << s.capacity() << endl;// 15
s.reserve(100);
cout << s.size() << endl;// 11
cout << s.capacity() << endl;// 111
s.reserve(50);
cout << s.size() << endl;// 11
cout << s.capacity() << endl;// 111
}
为string预留空间,不改变有效元素个数,当reserve的参数小于 string的底层空间总大小时,reserver不会改变容量大小。
如果提前已经知道string中大概要放多少个元素,可以提前将string中空间设置好,避免频繁扩容。
6.resize:将有效字符的个数该成n个,多出的空间用字符c填充
void resize (size_t n);
void resize (size_t n, char c);
int main()
{
string s;//空字符串
// 将s中有效字符个数增加到10个,多出位置用'a'进行填充
s.resize(10, 'a');
cout << s.size() << endl; // 10
cout << s.capacity() << endl;// 15
cout << s << endl; // “aaaaaaaaaa”
// 将s中有效字符个数增加到15个,多出位置用缺省值'\0'进行填充
// 注意此时s中有效字符个数已经增加到15个
s.resize(15);
cout << s.size() << endl;// 15
cout << s.capacity() << endl;// 15
cout << s << endl; // "aaaaaaaaaa\0\0\0\0\0"
// 将s中有效字符个数缩小到5个
s.resize(5);
cout << s.size() << endl;// 5
cout << s.capacity() << endl;// 15
cout << s << endl; // "aaaaa"
return 0;
}
resize(size_t n) 与 resize(size_t n, char c)都是将字符串中有效字符个数改变到n个,不同的是当字 符个数增多时:resize(n)用 0 来填充多出的元素空间,resize(size_t n, char c)用字符c来填充多出的 元素空间。
注意:resize在改变元素个数时,如果是将元素个数增多,可能会改变底层容量的大小,如果是将元素个数减少,底层空间总大小不变。
函数名称 | 功能说明 |
---|---|
operator[] | 返回pos位置的字符,const string类对象调用 |
begin+ end | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的迭代器 |
rbegin + rend | begin获取一个字符的迭代器 + end获取最后一个字符下一个位置的逆向迭代器 |
范围for | C++11支持更简洁的范围for的新遍历方式 |
1.operator[]:返回pos位置的字符,const string类对象调用
char& operator[] (size_t pos);
const char& operator[] (size_t pos) const;
int main()
{
string s1("hello world");
const string s2("Hello world");
cout << s1 << " " << s2 << endl;//hello world Hello world
cout << s1[0] << " " << s2[0] << endl;//h H
s1[0] = 'H';
cout << s1 << endl;//Hello world
// s2[0] = 'h'; 代码编译失败,因为const类型对象不能修改
}
2.string类对象的遍历操作
3种遍历方式:①for+operator[] ②迭代器 ③范围for
iterator begin();
const_iterator begin() const;
iterator end();
const_iterator end() const;
reverse_iterator rbegin();
const_reverse_iterator rbegin() const;
reverse_iterator rend();
const_reverse_iterator rend() const;
int main()
{
string s("hello world");
// 3种遍历方式:
// 需要注意的以下三种方式除了遍历string对象,还可以遍历是修改string中的字符,
// 另外以下三种方式对于string而言,第一种使用最多
// 1. for+operator[]
for (size_t i = 0; i < s.size(); ++i)
cout << s[i];
//hello world
cout << endl;
// 2.迭代器
string::iterator it = s.begin();
while (it != s.end())
{
cout << *it;
++it;
}
//hello world
cout << endl;
// string::reverse_iterator rit = s.rbegin();
// C++11之后,直接使用auto定义迭代器,让编译器推到迭代器的类型
// 逆置打印
auto rit = s.rbegin();
while (rit != s.rend())
{
cout << *rit;
++rit;
}
//dlrow olleh
cout << endl;
// 3.范围for
for (auto ch : s)
cout << ch;
//hello world
return 0;
}
函数名称 | 功能说明 |
---|---|
push_back | 在字符串后尾插字符c |
append | 在字符串后追加一个字符串 |
insert | 在字符串pos位置插入一个字符或字符串 |
erase | 删除字符串pos位置之后的len个字符 |
operator+= | 在字符串后追加字符串str |
c_str | 返回C格式字符串 |
find | 从字符串中pos位置从前向后找字符c,返回该字符在字符串中的位置 |
rfind | 从字符串中pos位置从后向前找字符c,返回该字符在字符串中的位置 |
substr | 在str中从pos位置开始,截取n个字符,然后将其返回 |
1.push_back:在字符串后尾插字符c
void push_back (char c);
string s1("hello");
s1.push_back('!');
cout << s1 << endl; //hello!
2.append:在字符串后追加一个字符串
string& append (const string& str);//追加str
string& append (const string& str, size_t subpos, size_t sublen);
//追加str从subpos位置开始的向后sublen个字符
string& append (const char* s);//追加字符串s
string& append (const char* s, size_t n);//追加字符串s的n个字符
string& append (size_t n, char c);//追加n个c字符
string s1("hello");
string s2(" world");
s1.append(s2); //hello world
s1.append(s2, 1, 5);//hello worldworld
s1.append("!!!");//hello worldworld!!!
s1.append("12345", 3);//hello worldworld!!!123
s2.append(3, '!');// world!!!
3.insert:在字符串pos位置插入一个字符或字符串
string& insert (size_t pos, const string& str);
//在pos位置插入str
string& insert (size_t pos, const string& str, size_t subpos, size_t sublen);
//在pos位置插入str的从subpos位置开始的sublen个字符
string& insert (size_t pos, const char* s);
//在pos位置插入字符串s
string& insert (size_t pos, const char* s, size_t n);
//在pos位置插入字符串s的n个字符
string& insert (size_t pos, size_t n, char c);
//在pos位置插入n个字符c
string s1("012345");
string s2("xxabxxcd");
string s3("**");
s1.insert(1,s3);//0**12345
s1.insert(3, s2, 0,2);//0**xx12345
s3.insert(2,"11");//**11
s3.insert(4, "2234", 3);//**11223
s3.insert(1, 3, 'x'); //*xxx*11223
4.operator+=:在字符串后追加字符串str
string& operator+= (const string& str);
string& operator+= (const char* s);
string& operator+= (char c);
string s1("hello");
string s2("world");
s1 += s2;//helloworld
s1 += "123"; //helloworld123
s1 += '!'; //helloworld123!
在string尾部追加字符时,s.push_back(c) / s.append(1, c) / s += 'c'三种的实现方式差不多,一般 情况下string类的+=操作用的比较多,+=操作不仅可以连接单个字符,还可以连接字符串。
5.c_str:返回C格式字符串
const char* c_str() const;
string s1("hello world");
const char* pstr = s1.c_str();
cout << pstr << endl;//hello world
返回一个指向数组的指针,该数组包含一个以null结尾的字符序列(即C字符串),表示字符串对象的当前值。
6.在str中从pos位置开始,截取n个字符,然后将其返回
string substr (size_t pos = 0, size_t len = npos) const;
npos是string里面的一个静态成员变量
static const size_t npos = -1;为size_t类型的最大值,一般用于表示到字符串的末尾。
string s1("hello world");
string s2 = s1.substr();
cout << s2 << endl;//hello world
7.find,rfind:从字符串中pos位置从前(后)向后(前)找字符c,返回该字符在字符串中的位置。
size_t find (const string& str, size_t pos = 0) const;
size_t find (const char* s, size_t pos = 0) const;
size_t find (char c, size_t pos = 0) const;
size_t rfind (const string& str, size_t pos = npos) const;
size_t rfind (const char* s, size_t pos = npos) const;
size_t rfind (char c, size_t pos = npos) const;
string s1("hello,world!!");
string s2("world");
cout << s1.find(s2) << endl;//6
cout << s1.find("world") << endl;//6
cout << s1.find('!') << endl;//11
cout << s1.rfind(s2) << endl;//6
cout << s1.rfind("world") << endl;//6
cout << s1.rfind('!') << endl;//12
函数 | 功能说明 |
---|---|
operator+ | 尽量少用,因为传值返回,导致深拷贝效率低 |
operator>> | 输入运算符重载 |
operator<< | 输出运算符重载 |
getline | 获取一行字符串 |
relational operators | 大小比较 |
string类的其他接口就不一一列举和介绍了,有需要查文档即可。
为了与原来的string类进行区分,我们在命名空间中模拟实现:
#include
#include
using namespace std;
namespace sss//命名空间
{
class string
{
public:
string(const char* str = "")//构造函数
{}
string(const string& str)//拷贝构造函数
{}
string& operator=(const string& s)//赋值运算符重载函数
{}
~string()//析构函数
{}
private:
char* _str;
size_t _size;//有效字符个数
size_t _capacity;//存储有效字符串的空间大小(不包含'\0')
static const size_t npos;//表示字符串末尾
};
const size_t string::npos = -1;
}
1.构造函数
我们一般会用一个常量字符串来进行初始化,因此用const char* str的指针接收。
如果str为空字符串,即_size = 0时,多开辟几个字节的空间,防止后续按capacity倍数扩容时,出现capacity=0的情况。
string(const char* str = "")//构造函数
:_size(strlen(str))
{
_capacity = _size == 0 ? 3 : _size;
_str = new char[(_capacity + 1)];//多开一个空间用于存放'\0';
strcpy(_str, str);
}
2.析构函数
释放开辟的空间,成员变量置空即可。
~string()//析构函数
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
3.拷贝构造函数
我们来调用下方的test1()函数。
using namespace std;
namespace sss
{
class string
{
public:
string(const char* str = "")//构造函数
:_size(strlen(str))
{
_capacity = _size == 0 ? 3 : _size;
_str = new char[(_capacity + 1)];//多开一个空间用于存放'\0';
strcpy(_str, str);
}
~string()//析构函数
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
private:
char* _str;
size_t _size;//有效字符个数
size_t _capacity;//存储有效字符串的空间大小(不包含'\0')
static const size_t npos;//表示字符串末尾
};
const size_t string::npos = -1;
void test1()
{
string s1("hello world");
string s2(s1);
}
}
此处发生崩溃,上述string类没有显式定义其拷贝构造函数与赋值运算符重载,此时编译器会合成默认的,当用s1构造s2时,编译器会调用默认的拷贝构造。
调用默认的拷贝构造后,导致的问题是:s1、s2共用同一块内存空间,在释放时同一块空间被释放多次而引起程序崩溃,这种拷贝方式,称为浅拷贝。
浅拷贝:
编译器只是将对象中的值拷贝过来。如果对象中管理资源,最后就会导致多个对象共享同一份资源,当一个对象销毁时就会将该资源释放掉,而此时另一些对象不知道该资源已经被释放,以为 还有效,所以当继续对资源进项操作时,就会发生发生了访问违规。
可以采用深拷贝解决浅拷贝问题,即:每个对象都有一份独立的资源,不要和其他对象共享。
深拷贝:
如果一个类中涉及到资源的管理,其拷贝构造函数、赋值运算符重载以及析构函数必须要显式给出。一般情况都是按照深拷贝方式提供。
先拷贝其大小和容量,再开辟一块新的空间,将其字符串拷贝过去。
string(const string& s)//拷贝构造函数
:_size(s._size)
, _capacity(s._capacity)
{
_str = new char[(_capacity + 1)];
strcpy(_str, s._str);
}
4.赋值运算符重载函数
新开辟一块和原字符串相等的空间,将字符串拷贝过去,在释放掉其原本指向的空间。
string& operator=(const string& s)//赋值运算符重载函数
{
if (this != &s)//防止出现自己给自己赋值的情况
{
char* tmp = new char[(s._capacity + 1)];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
string类中的迭代器可以把它成是一个char* 的指针。
typedef char* iterator;
typedef const char* const_iterator;
iterator begin();//返回第一个字符的位置
iterator end();//返回最后一个字符的下一个位置
const_iterator begin()const;
const_iterator end()const;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin()const
{
return _str;
}
const_iterator end()const
{
return _str + _size;
}
使用演示:
void test2()
{
string s1("hello world");
string::iterator it = s1.begin();
while (it != s1.end())
{
*it += 1;//const迭代器只能读,不能修改
cout << *it;
++it;
}
//输出结果:ifmmp!xpsme
}
1.大小和容量
size_t size()const//返回当前字符串的有效个数
{
return _size;
}
size_t capacity()const//返回当前字符串存储空间的大小
{
return _capacity;
}
bool empty()const//判断字符串是否为空
{
return 0 == _size;
}
2.reserve 和 resize 函数
void reserve(size_t newCapacity)//预留空间
{
// 如果新容量大于旧容量,则开辟空间
if (newCapacity > _capacity)
{
char* str = new char[newCapacity + 1];
strcpy(str, _str);
// 释放原来旧空间,然后使用新空间
delete[] _str;
_str = str;
_capacity = newCapacity;
}
}
void resize(size_t newSize, char c = '\0')
{
if (newSize > _size)
{
// 如果newSize大于底层空间大小,则需要重新开辟空间
if (newSize > _capacity)
{
reserve(newSize);
}
memset(_str + _size, c, newSize - _size);
}
_size = newSize;
_str[newSize] = '\0';
}
1.push_back()函数
void push_back(char c)
{
if (_size == _capacity)//判断是否需要扩容
reserve(_capacity * 2);
//调用reserve预留空间
_str[_size++] = c;
_str[_size] = '\0';
}
2.insert()函数
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char ch)
{
assert(pos <= _size);//确保pos合法
if (_size + 1 > _capacity)
{
reserve(_capacity * 2);
}
size_t end = _size + 1;
while (end > pos)//将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 (_size + len > _capacity)
reserve(_size + len);
size_t end = _size + len;
while (end > pos + len - 1)//将pos位置之后的字符向后移动
{
_str[end] = _str[end - len];
--end;
}
strncpy(_str + pos, str, len);
_size += len;
return *this;
}
3.erase()函数
第一种情况删除pos位置后的全部字符,直接将pos位置改为'\0',将_size赋值为pos即可。(pos为字符串的下标)
第二种情况删除pos位置后的部分字符串,将待删除字符串后面的字符串向前移动即可。_size减去删除的长度len。
string& erase(size_t pos, size_t len = npos)
{
//npos表示字符串结束位置
assert(pos <= _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
4.append函数和 operator+=
append函数直接复用insert即可,即在字符串末尾追加一个字符串。
void append(const char* str)
{
insert(_size, str);
}
operator+=直接复用push_back 和 appen即可,即分布追加一个字符和字符串。
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char*str)
{
append(str);
return *this;
}
5.clear函数
清空字符串,直接将有效字符个数_size置为0,再将第一个字符置为'\0',将字符串置为空串。
void clear()
{
_size = 0;
_str[_size] = '\0';
}
6.swap函数
直接调用标准库里的swap函数,分布交换其成员变量即可。
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
7.c_str函数
返回c类型的字符串。
const char* c_str()const
{
return _str;
}
1.operator[]
返回字符串中下标为pos位置的元素。
char& operator[](size_t pos)
{
assert(pos < _size);
return _str[pos];
}
const char& operator[](size_t pos)const
{
assert(pos < _size);
return _str[pos];
}
2.find()函数
size_t find(char ch, size_t pos = 0)
{
assert(pos <= _size);//确保pos合法
for (size_t i = pos; i < _size; ++i)
{
if (_str[i] == ch)
return i;
}
return npos;
}
size_t find(const char* str, size_t pos = 0)
{
assert(pos <= _size);
char* p = strstr(_str + pos, str);//调用库函数
if (p == nullptr)
return npos;
else
return p - _str;//得到的元素个数,即为目标字符串开头的下标
}
字符串比较调用标准库的strcmp函数,其余对已经实现的运算符重载进行复用。
bool operator>(const string& s)const
{
return strcmp(_str, s._str)>0;
}
bool operator==(const string& s)const
{
return strcmp(_str, s._str) == 0;
}
bool operator>=(const string& s)const
{
return (* this>s || *this == s);
}
bool operator<(const string& s)const
{
return !(* this >= s);
}
bool operator<=(const string& s)const
{
return !(*this >s);
}
bool operator!=(const string& s)const
{
return !(*this == s);
}
流插入:逐个字符输出。
ostream& operator<<(ostream& _cout, const sss::string& s)
{
// 直接cout时, 是将_str当成char*打印的,遇到内部的\0时后序内容就不打印了
//cout << s._str;
for (size_t i = 0; i < s.size(); ++i)
{
_cout << s[i];
}
return _cout;
}
流提取:
istream& operator>>(istream& in, string& s)
{
s.clear();//清空字符串
char ch = in.get();//读取一个字符
char buff[128];//用做缓存数组,避免频繁开辟空间
size_t i = 0;
while (ch != ' ' && ch != '\n')//这两个符号为分割符
{
buff[i++] = ch;
if (i == 127)//当数组满了,将字符串写入目标串
{
buff[127] = '\0';
s += buff;
i = 0;
}
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
#include
#include
using namespace std;
namespace sss
{
class string
{
public:
string(const char* str = "")//构造函数
:_size(strlen(str))
{
_capacity = _size == 0 ? 3 : _size;
_str = new char[(_capacity + 1)];//多开一个空间用于存放'\0';
strcpy(_str, str);
}
string(const string& s)//拷贝构造函数
:_size(s._size)
, _capacity(s._capacity)
{
_str = new char[(_capacity + 1)];
strcpy(_str, s._str);
}
string& operator=(const string& s)//赋值运算符重载函数
{
if (this != &s)
{
char* tmp = new char[(s._capacity + 1)];
strcpy(tmp, s._str);
delete[] _str;
_str = tmp;
_size = s._size;
_capacity = s._capacity;
}
return *this;
}
~string()//析构函数
{
delete[] _str;
_str = nullptr;
_size = _capacity = 0;
}
typedef char* iterator;
typedef const char* const_iterator;
iterator begin()
{
return _str;
}
iterator end()
{
return _str + _size;
}
const_iterator begin()const
{
return _str;
}
const_iterator end()const
{
return _str + _size;
}
size_t size()const
{
return _size;
}
size_t capacity()const
{
return _capacity;
}
bool empty()const
{
return 0 == _size;
}
void reserve(size_t newCapacity)
{
// 如果新容量大于旧容量,则开辟空间
if (newCapacity > _capacity)
{
char* str = new char[newCapacity + 1];
strcpy(str, _str);
// 释放原来旧空间,然后使用新空间
delete[] _str;
_str = str;
_capacity = newCapacity;
}
}
void resize(size_t newSize, char c = '\0')
{
if (newSize > _size)
{
// 如果newSize大于底层空间大小,则需要重新开辟空间
if (newSize > _capacity)
{
reserve(newSize);
}
memset(_str + _size, c, newSize - _size);
}
_size = newSize;
_str[newSize] = '\0';
}
void push_back(char c)
{
if (_size == _capacity)
reserve(_capacity * 2);
_str[_size++] = c;
_str[_size] = '\0';
}
// 在pos位置上插入字符c/字符串str,并返回该字符的位置
string& insert(size_t pos, char ch)
{
assert(pos <= _size);
if (_size + 1 > _capacity)
{
reserve(_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 (_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& erase(size_t pos, size_t len = npos)
{
assert(pos <= _size);
if (len == npos || pos + len >= _size)
{
_str[pos] = '\0';
_size = pos;
}
else
{
strcpy(_str + pos, _str + pos + len);
_size -= len;
}
return *this;
}
void append(const char* str)
{
insert(_size, str);
}
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
string& operator+=(const char* str)
{
append(str);
return *this;
}
void clear()
{
_size = 0;
_str[_size] = '\0';
}
void swap(string& s)
{
std::swap(_str, s._str);
std::swap(_size, s._size);
std::swap(_capacity, s._capacity);
}
const char* c_str()const
{
return _str;
}
char& operator[](size_t index)
{
assert(index < _size);
return _str[index];
}
const char& operator[](size_t index)const
{
assert(index < _size);
return _str[index];
}
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;
}
size_t find(const char* str, size_t pos = 0)
{
assert(pos <= _size);
char* p = strstr(_str + pos, str);
if (p == nullptr)
return npos;
else
return p - _str;
}
bool operator>(const string& s)const
{
return strcmp(_str, s._str) > 0;
}
bool operator==(const string& s)const
{
return strcmp(_str, s._str) == 0;
}
bool operator>=(const string& s)const
{
return (*this > s || *this == s);
}
bool operator<(const string& s)const
{
return !(*this >= s);
}
bool operator<=(const string& s)const
{
return !(*this > s);
}
bool operator!=(const string& s)const
{
return !(*this == s);
}
private:
friend ostream& operator<<(ostream& _cout, const sss::string& s);
friend istream& operator>>(istream& _cin, sss::string& s);
private:
char* _str;
size_t _size;//有效字符个数
size_t _capacity;//存储有效字符串的空间大小(不包含'\0')
static const size_t npos;//表示字符串末尾
};
const size_t string::npos = -1;
ostream& operator<<(ostream& _cout, const sss::string& s)
{
// 不能使用这个, 因为string的字符串内部可能会包含\0
// 直接cout时, 是将_str当成char*打印的,遇到内部的\0时后序内容就不打印了
//cout << s._str;
for (size_t i = 0; i < s.size(); ++i)
{
_cout << s[i];
}
return _cout;
}
istream& operator>>(istream& in, string& s)
{
s.clear();//清空字符串
char ch = in.get();//读取一个字符
char buff[128];//用做缓存数组,避免频繁开辟空间
size_t i = 0;
while (ch != ' ' && ch != '\n')//这两个符号为分割符
{
buff[i++] = ch;
if (i == 127)//当数组满了,将字符串写入目标串
{
buff[127] = '\0';
s += buff;
i = 0;
}
}
if (i != 0)
{
buff[i] = '\0';
s += buff;
}
return in;
}
}