C+±string
string
是 C++ 标准库中提供的字符串类,定义在 <string>
头文件中,属于 std
命名空间。该类的接口与常规容器接口基本一致,还有一些属于 string
的额外操作。
1. 底层理解
string 底层是一个存储字符的顺序表。
cpp
class string{
char* _str; // 字符串的起始地址
size_t _size; // 下一个字符存储的位置
size_t capacity; // 当前容量
};

stirng 底层实际开辟 capacity + 1 大小,为了兼容 C 保留
'\0'
。
2. 成员函数
2.1 构造函数
序号 | 函数原型 | 说明 |
---|---|---|
1️⃣ | string() | 默认构造函数,构造一个空的 string 对象,但是有一个 '\0' (兼容C) |
2️⃣ | string(const string& str) | 拷贝构造 |
3️⃣ | string(const string& str , size_t pos , size_t len = npos) | 拷贝部分 str ,从 pos 位置开始拷贝 len 个(len 超出 str 实际大小或为 npos 拷贝至结尾) |
4️⃣ | string(const char * s) | 使用 C 风格字符串构造 string 对象 |
5️⃣ | string(const char * s , size_t n) | 使用 s 中前 n 个字符构造 string 对象 |
6️⃣ | string(size_t n , char c) | 使用 n 个 c 字符构造 string 对象 |
7️⃣ | template <class InputIterator> string (InputIterator first , InputIterator last) | 使用一段迭代器区间构造 string 对象 |
string::npos :
static const size_t npos = -1;
2.2 赋值重载
序号 | 函数原型 | 说明 |
---|---|---|
1️⃣ | string& operator= (const string& str) | 将 str 对象赋值给当前 string 对象 |
2️⃣ | string& operator= (const char * s) | 将 C 风格字符串赋值给当前 string 对象 |
3️⃣ | string& operator= (char c) | 将 c 字符赋值给当前 string 对象 |
2.3 迭代器
序号 | 函数原型 | 说明 |
---|---|---|
1️⃣ | iterator begin() | 返回指向 string 对象中第一个字符的迭代器 |
2️⃣ | const_iterator begin() const | 返回指向 string 对象中第一个字符的 const 迭代器 |
3️⃣ | iterator end() | 返回指向 string 对象结尾的下一个位置的迭代器 |
4️⃣ | const_iterator end() const | 返回指向 string 对象结尾的下一个位置的 const 迭代器 |
5️⃣ | reverse_iterator rbegin() | 返回指向 string 对象结尾位置的迭代器 |
6️⃣ | const_reverse_iterator rbegin() const | 返回指向 string 对象结尾位置的 const 迭代器 |
7️⃣ | reverse_iterator() rend() | 返回指向 string 对象起始的前一个位置的迭代器 |
8️⃣ | const_reverse_iterator() rend() const | 返回指向 string 对象起始的前一个位置的 const 迭代器 |
范围 for:
-
范围for的本质就是迭代器。
-
范围for可以遍历普通数组和容器对象。
cpp
// 元素的访问
for (auto c : str) {
cout << c << str;
}
// 元素的修改
for (auto& c : str) {
c *= 2;
}
2.4 容量相关的接口
序号 | 函数原型 | 说明 |
---|---|---|
1️⃣ | size_t size() const | 返回 string 的长度 |
2️⃣ | size_t capacity() const | 返回 string 的容量 |
3️⃣ | void resize (size_t n) | 1. n > size(): 将 size 设置为 n,并将 size ~ n 下标区间元素填充 '\0' 2. n < size(): 将 size 设置为 n,n后的元素被删除 |
4️⃣ | void resize (size_t n , char c) | 1. n > size(): 将 size 设置为 n,并将 size ~ n 下标区间元素填充 c 2. n < size(): 将 size 设置为 n,n后的元素被删除 |
5️⃣ | void reserve (size_t n = 0) | 修改 string 对象的 capacity() 至 n |
6️⃣ | void clear(); | 清空 string 对象的内容 |
7️⃣ | bool empty() const | 判断当前 string 对象是否为空 |
resize 和 reserve的区别 :
2.5 元素的访问
序号 | 函数原型 | 说明 |
---|---|---|
1️⃣ | char& operator[] (size_t pos) | 返回 string 对象中 pos 位置字符的引用 |
2️⃣ | const char& operator[](size_t pos) const | 返回 const string 对象中 pos 位置字符的引用 |
3️⃣ | char& at (size_t pos) | 返回 string 对象中 pos 位置字符的引用 |
4️⃣ | const char& at (size_t pos) const | 返回 const string 对象中 pos 位置字符的引用 |
5️⃣ | char& back() | 返回 string 对象最后一个字符的引用 |
6️⃣ | const char& back() const | 返回 const string 对象最后一个字符的引用 |
7️⃣ | char& front() | 返回 string 对象第一个字符的引用 |
8️⃣ | const char& front() const | 返回 const string 对象第一个字符的引用 |
\] 和 at 的区别:\[\]断言检查pos位置是否合法,at是抛异常。
2.6 修改相关的接口
序号 | 函数原型 | 说明 |
---|---|---|
1️⃣ | void push_back (char c) | 在当前 string 对象的末尾追加 c 字符 |
2️⃣ | string& append (const string& str) | 在 string 对象的末尾追加 str 对象 |
3️⃣ | string& append (const string& str , size_t subpos , size_t sublen) | 在 string 对象的末尾追加 str 对象的子串,从 subpos 位置开始,拷贝 sublen 个字符过去 |
4️⃣ | string& insert (size_t pos, const string& str); | 在 string 对象的 pos 位置添加 str 对象 |
5️⃣ | string& insert (size_t pos, size_t n, char c); | 在 string 对象的 pos 位置添加 n 个 c 字符 |
6️⃣ | string& erase (size_t pos = 0, size_t len = npos); | 从 string 对象的 pos 位置删除 len 个字符(若len = npos删除至结尾) |
7️⃣ | string& operator+= (const string& str); | 在 string 对象的末尾追加 str 对象 |
8️⃣ | *string& operator+= (const char s);** | 在 string 对象的末尾追加 s 字符串 |
9️⃣ | string& operator+= (char c); | 在 string 对象的末尾追加 c 字符 |
2.7 其他接口
序号 | 函数原型 | 说明 |
---|---|---|
1️⃣ | const char c_str() const* | 将 string 对象以C风格字符串返回 |
2️⃣ | size_t find (char c , size_t pos = 0) const | 从当前 string 对象的 pos 位置开始查找 c 字符,返回这个字符第一次出现的位置,否则返回 string::npos |
3️⃣ | size_t rfind (char c , size_t pos = npos) const | 从当前 string 对象的 pos 位置从后向前开始查找 c 字符,返回这个字符最后一次出现的位置,否则返回 string::npos |
4️⃣ | string substr (size_t pos = 0 , size_t len = npos) const | 返回 string 对象 pos 位置开始的 len 个字符的子串 |
3. 非成员函数
序号 | 函数原型 | 说明 |
---|---|---|
1️⃣ | istream& getline (istream& is , string& str , char delim) | 获取一行字符遇到 delim 字符终止 |
2️⃣ | istream& getline (istream& is , string& str) | 获取一行字符遇到 \n 终止 |
3️⃣ | relational operators | 关系比较(>、<、>=、<=、!=、==) |
4️⃣ | istream& operator>> (istream& is, string& str); | 流提取运算符重载 |
5️⃣ | ostream& operator<< (ostream& os, const string& str); | 流插入运算符重载 |
4. string模拟实现
cpp
// string.hpp
#pragma once
#include <iostream>
#include <cstring>
#include <cassert>
namespace simulateSTL
{
class string
{
public:
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; }
void clear() { _size = 0; _str[_size] = 0; }
bool empty() const { return _size == 0; }
char& operator[](size_t pos) { assert(pos < _size); return _str[pos]; }
const char& operator[](size_t pos) const { assert(pos < _size); return _str[pos]; }
const char* c_str() const { return _str; }
bool operator==(const string& str) const { return !strcmp(_str , str._str); }
bool operator!=(const string& str) const { return !(*this == str); }
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); }
void swap (string& str) { std::swap(_str , str._str);std::swap(_size , str._size);std::swap(_capacity , str._capacity); }
public:
string(const char *str = "");
string(const string& str);
string& operator=(string str);
~string();
void reserve(size_t n);
void resize(size_t n , char c = '\0');
void push_back(char c);
void pop_back();
string& append(const char* s);
string& operator+=(const string& str);
string& operator+=(char c);
string& insert(size_t pos , char c);
string& insert(size_t pos , const char* s);
string& erase(size_t pos);
string substr(size_t pos = 0 , size_t len = npos) const;
size_t find(char c , size_t pos = 0) const;
private:
char *_str = nullptr;
size_t _size = 0;
size_t _capacity = 0;
public:
static const size_t npos = -1;
};
string::string(const char *str)
: _size(strlen(str))
{
_str = new char[_size + 1]; // 兼容C保留'\0'位置
strcpy(_str, str);
_capacity = _size;
}
// 现代写法
string::string(const string& str) {
string temp(str._str);
swap(temp);
}
// 现代写法
string& string::operator=(string str) {
swap(str);
return *this;
}
string::~string()
{
// C++中可以delete nullptr
delete[] _str;
_size = _capacity = 0;
}
void string::reserve(size_t n) {
if (n > _capacity) {
char* temp = new char[n + 1]; // 兼容C保留'\0'
strcpy(temp , _str);
delete[] _str;
_str = temp;
_capacity = n;
}
}
void string::resize(size_t n , char c) {
if (n <= _size) {
_size = n;
_str[_size] = 0;
} else {
if (n > _capacity) {
reserve(n);
}
while (_size < n) {
_str[_size++] = c;
}
_str[_size] = 0;
}
}
void string::push_back(char c) {
if (_size == _capacity) {
reserve(_capacity == 0 ? 4 : _capacity * 2);
}
_str[_size++] = c;
_str[_size] = 0;
}
void string::pop_back() {
assert(!empty());
_str[--_size] = 0;
}
string& string::append(const char* s) {
size_t len = strlen(s);
if (_size + len > _capacity) {
reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
}
for (size_t i = 0; i < len; i++) {
_str[_size++] = s[i];
}
_str[_size] = 0;
return *this;
}
string& string::operator+=(const string& str) {
return append(str._str);
}
string& string::operator+=(char c) {
push_back(c);
return *this;
}
string& string::insert(size_t pos , char c) {
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] = c;
_size++;
return *this;
}
string& string::insert(size_t pos , const char* s) {
assert(pos <= _size);
size_t len = strlen(s);
if (_size + len > _capacity) {
reserve(_size + len > _capacity * 2 ? _size + len : _capacity * 2);
}
size_t end = _size + len;
while (end > pos + len - 1) {
_str[end] = _str[end - len];
end--;
}
strncpy(_str + pos , s , len);
_size += len;
return *this;
}
string& string::erase(size_t pos) {
assert(pos < _size);
size_t end = pos;
while (end < _size) {
_str[end] = _str[end + 1];
end++;
}
_size--;
return *this;
}
string string::substr(size_t pos , size_t len) const {
assert(pos < _size);
// 修正len
if (len + pos > _size || len == npos) {
len = _size - pos;
}
string sub;
sub.reserve(len);
for (size_t i = 0; i < len; i++) {
sub += _str[pos + i];
}
return sub;
}
size_t string::find(char c , size_t pos) const {
assert(pos < _size);
while (pos < _size) {
if (_str[pos] == c) return pos;
pos++;
}
return npos;
}
// 全局运算符重载函数
std::ostream& operator<<(std::ostream& out, const string& s) {
for (auto ch : s) {
out << ch;
}
return out;
}
std::istream& operator>>(std::istream& in, string& s) {
s.clear();
// 定义缓冲区
const int N = 256;
char buff[N];
int i = 0; // 0 ~ 255
char ch;
ch = in.get(); // 获取单个字符
while (ch != ' ' && ch != '\n') {
buff[i++] = ch;
if (i == N-1) {
buff[i] = '\0';
s += buff;
i = 0;
}
ch = in.get();
}
if (i > 0) {
buff[i] = '\0';
s += buff;
}
return in;
}
} // namespace end