> 文章列表 > C++-STL-string

C++-STL-string

C++-STL-string

STL-string

  • string的使用
    • 默认成员函数
      • 构造函数
      • 赋值运算符重载
      • 析构函数
    • capcity相关函数
    • 访问相关函数
    • 修改string相关函数
    • string类其他功能函数
    • 非成员函数
  • string的模拟实现

string的使用

在C语言中,没有类这个概念,处理字符串通常是把字符串存在字符数组中处理的,并且库中提供了大量的字符串处理函数,但是其实与C++相比显的并不是很好用,C++中封装了string这个类,底层也是字符数组,在这基础上类内封装了大量的处理字符串的方法,
下面来一一的介绍一下。
首先推荐一个C++文档的网站:cplusplus
C++-STL-string
可以看到string类其实是typedef出来的,最初的那个类叫做basic_string这个类,由于编码的形式有很多种,一个字符占用的空间不同有的1个字节的2个字节有的4个字节,所以有不同的string类。
C++-STL-string
最常用的还是basic_string< char >这个类。

默认成员函数

构造函数

C++-STL-string
可以看到构造函数的重载形式非常多,下面我只演示最常用的几个。
1,使用字符串初始化

int main()
{string str = "hello string";cout << str << endl;return 0;
}

C++-STL-string
2,拷贝构造

int main()
{string str = "hello string";cout << str << endl;string str1(str);cout << str1 << endl;return 0;
}

C++-STL-string
3,使用string类对象的一段区间构造

int main()
{string str1 = "hello string";string str2(str1, 0, 8);cout << str2 << endl;return 0;
}

C++-STL-string

4,使用字符串的前几个字符构造

int main()
{string str1("hello string", 5);cout << str1 << endl;return 0;
}

C++-STL-string

赋值运算符重载

C++-STL-string
1,string类对象作参数

int main()
{string str1;string str2 = "hello C++";str1 = str2;cout << str1 << endl;return 0;
}

C++-STL-string

2,字符串做参数

int main()
{string str1;str1 = "hello C++";cout << str1 << endl;return 0;
}

C++-STL-string

3,单个字符作参数

int main()
{string str1;str1 = 'C';cout << str1 << endl;return 0;
}

C++-STL-string

析构函数

C++-STL-string

capcity相关函数

C++-STL-string

  • size与length
int main()
{string str1 = "hello world";cout << "size() : " << str1.size() << endl<<  "length() : " << str1.length() << endl;return 0;
}

C++-STL-string
其实这两个函数功能是重叠的,实现是一摸一样的,字符串用length更合理一些,但是后面的容器都是使用的size,所以为了统一string类也提供了size这个接口。

  • max_size
    C++-STL-string
    字符串能达到的最大长度,其实返回的是无符号整型的最大值。

C++-STL-string

  • capcity
    capcity就是返回其容量

  • resize与reserve
    C++-STL-string
    resize 是重新调整string的大小,如果传入的参数大于当前的大小,那么就会扩容加初始化,如果小于当前的大小就会改变其有效长度(容量不变)。

int main()
{string str1 = "hello world";str1.resize(20);str1.resize(5);return 0;
}

C++-STL-string
C++-STL-string
C++-STL-string
C++-STL-string

reserve重新调整其容量,只会在传入参数大于当前容量时起作用,传入参数大于当前容量时会扩容不加初始化。

int main()
{string str1 = "hello world";cout << str1.capacity() << endl;str1.reserve(5);cout << str1.capacity() << endl;str1.reserve(20);cout << str1.capacity() << endl;return 0;
}

C++-STL-string

  • clear
    C++-STL-string
    对string做清空

  • empty
    C++-STL-string
    查看string是否为空

访问相关函数

C++-STL-string
最常用的就是[ ]访问string的任意元素,at()与其功能类似只是对越界的检查方式不同,operator[ ]采用的是断言方式检查,at采用的是温柔的检查方式。

int main()
{string str1("hello world");for (int i = 0; i < str1.size(); i++){cout << str1[i] << " ";}cout << endl;for (int i = 0; i < str1.size(); i++){cout << str1.at(i) << " ";}cout << endl;return 0;
}

C++-STL-string

修改string相关函数

C++-STL-string
1,append与operator+=
C++-STL-string
C++-STL-string
两个功能是一样的,只是append的接口更多一些,但是append并没有operator+=更常用一些。
2,尾插和尾删
C++-STL-string
C++-STL-string
3,任意位置插入删除
C++-STL-string
C++-STL-string
4,交换两个string对象
C++-STL-string

string类其他功能函数

C++-STL-string
1,c_str
C++-STL-string
返回底层维护字符串的地址。

int main()
{string str1("hello string");const char* s = str1.c_str();cout << s << endl;return 0;
}

C++-STL-string
2,find 与 rfind
C++-STL-string
C++-STL-string

一个是从前往后查找,一个是从后往前查找,查找的内容可以是,字符,字符串,string类对象。返回的是查找到的字串的位置下标。

3.substr
C++-STL-string

字串切割函数,将一个string类对象从pos位置开始长度为len的字串返回,这里的nops是无符号整型的最大值。如果长度大于原字符串本身那么就将从pos位置开始直到结尾的字串返回。

C++-STL-string

4,compare
C++-STL-string
这个函数其实也并不常用,主要因为string类重载了大量的关系运算符。
C++-STL-string

非成员函数

C++-STL-string
1,重载流提取和流插入操作符

由于string类是一个自定义类型,无法使用cin,cout对其进行输入和输出,所以要对>>和<<操作符进行重载。

2,getline
C++-STL-string

使用cin>>读取string的时候,遇到空格就会停止,所以我们想输入一个带有空格的字符串就会出现问题,而getline这个函数就可以避免这个问题,它默认是以读取到’\\n’为结束标志的,也给以手动改变其结束标志。

string的模拟实现

1,大体框架

namespace gy
{class string{public:string(const char* str = ""):_size(strlen(str)){_capcity = _size == 0 ? 4 : _size;_str = new char[_capcity + 1];strcpy(_str, str);}private:char* _str;size_t _size;size_t _capcity;static const size_t npos = -1;};
}

2,拷贝构造函数

string(const string& s):_size(s._size), _capcity(s._capcity)
{_str = new char[_capcity + 1];strcmp(_str, s._str);
}

3,赋值运算符重载

string& operator=(const string& s)
{_size = s._size;_capcity = s._capcity;char* tmp = new char[_capcity + 1];strcpy(tmp, s._str);delete[] _str;_str = tmp;return *this;
}

4,析构函数

~string()
{delete[] _str;_str = nullptr;_size = _capcity = 0;
}

5,c_str函数

const char* c_str()
{return _str;
}

6,size与capcity

size_t size() const
{return _size;
}
size_t capcity() const
{return _capcity;
}

由于这样的函数并不会对string的内容进行修改,最好加上const这样就能使普通对象和const对象都能调用。

7,operator[ ]

char& operator[](size_t n)
{assert(n < _size);return _str[n];
}
const char& operator[](size_t n) const
{assert(n < _size);return _str[n];
}

8,reserve

void reserve(size_t n)
{if (n > _capcity){char* tmp = new char[n + 1];strcpy(tmp, _str);delete[] _str;_str = tmp;_capcity = n;}
}

9,insert

insert 重载两个版本,一个是插入字符,一个是插入字符串

string& insert(size_t pos, char ch)
{assert(pos <= _size);//插入前检查是否需要扩容if (_size + 1 > _capcity)reserve(_size * 2);//挪动数据size_t end = _size;while (end >= pos){_str[end + 1] = _str[end];--end;}_str[pos] = ch;_size++;return *this;
}

如果采用这种写法,你在测试的是时候会发现程序一直在跑停不下来。
这是由于我们采用的下标都是size_t的类型的,如果pos为0,最后一次挪动数据的时候,end处于0位置,挪动完后–end,end会减成 -1,而-1对于无符号整数来说就是最大值,所以会造成死循环。所以我们得换种写法。

string& insert(size_t pos, char ch)
{assert(pos <= _size);//插入前检查是否需要扩容if (_size + 1 > _capcity)reserve(_size * 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* s)
{assert(pos <= _size);size_t lens = strlen(s);if (_size + lens > _capcity)reserve(_size + lens);//挪动数据size_t end = _size + lens;while (end > pos + lens - 1){_str[end] = _str[end - lens];--end;}strncpy(_str, s, lens);_size += lens;return *this;
}

10,erase

string& erase(size_t pos, size_t len = npos)
{assert(pos < _size);//挪动数据if (len == npos || _size - pos <= len){_str[pos] = '\\0';_size = pos;}else{size_t begin = pos + len;while (begin <= _size){_str[begin - len] = _str[begin];++begin;}_size -= len;}return *this;
}

11,push_back, pop_back, append

这些接口都可以使用上面的insert和erase复用。

void push_back(char ch)
{insert(_size, ch);
}
void pop_back()
{erase(_size - 1);
}
void append(const char* s)
{insert(_size, s);
}

12,operator+=

+=一个字符

void operator+=(char ch)
{push_back(ch);
}

+=字符串

void operator+=(const char* s)
{append(s);
}

这些函数都在复用insert和erase,可见insert和erase一旦实现,就已经完成了许多工作。

13,swap

成员函数的swap非常简单,只需要交换内部为何的_str,_size,_capcity即可。

void swap(string& s)
{std::swap(_str, s._str);std::swap(_size, s._size);std::swap(_capcity, s._capcity);
}

14,find

find 一个字符

size_t find(char ch,size_t pos = 0)
{assert(pos < _size);for (int i = pos; i < size(); i++){if (_str[i] == ch)return i;}return npos;
}

find 一个字符串

size_t find(const char* s, size_t pos = 0)
{assert(pos < _size);char* res = strstr(_str + pos, s);if (!res)return npos;return res - _str;
}

15,substr

string substr(size_t pos, size_t len = npos)
{assert(pos < _size);string res;size_t curlen = len;if (len <= _size - pos || len == npos)curlen = _size - pos;char* tmp = new char[curlen + 1];strncpy(tmp, _str + pos, curlen);tmp[curlen] = '\\0';res += tmp;delete[] tmp;return res;
}

16,重载关系运算符

bool operator==(const string& str) const
{return strcmp(_str, str._str) == 0;
}
bool operator>(const string& str) const
{return strcmp(_str, str._str) > 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);
}

17,重载流插入操作符

friend ostream& operator<<(ostream& _cout, const string& str);//类内
ostream& operator<<(ostream& _cout, const string& str)
{for (int i = 0; i < str.size(); i++){if (str[i] == '\\0')cout << ' ';elsecout << str[i];}return _cout;
}

18,重载流插入操作符

friend istream& operator>>(istream& _cin, string& str);//类内
istream& operator>>(istream& _cin, string& str)
{str.clear();char ch = cin.get();//缓冲区提高效率char tmp[128];int i = 0;while (ch != ' ' && ch != '\\n'){tmp[i++] = ch;if (i == 127){tmp[127] = '\\0';str += tmp;i = 0;}ch = cin.get();}if (i != 0){tmp[i] = '\\0';str += tmp;}return _cin;
}

18,正向迭代器

iterator begin()
{return _str;
}
iterator end()
{return _str + _size;
}
const_iterator begin() const
{return _str;
}
const_iterator end() const
{return _str + _size;
}