String (C++标准库)

是C++标准程式库中的一个头文件，定义了C++标准中的字符串的基本模板类std::basic_string及相关的模板类实例：

模板类实例｜std::basic_string的模板实参

`string`｜`char`

`wstring`｜`wchar_t`

`u16string`｜`char16_t`（C++11新增）

`u32string`｜`char32_t`（C++11新增）

其中的string是以char作为模板参数的模板类实例，把字符串的内存管理责任由`string`负责而不是由编程者负责，大大减轻了C语言风格的字符串的麻烦。

`std::basic_string`提供了大量的字符串操作函数，如比较、连接、搜索、替换、获得子串等。并可与C语言风格字符串双向转换。`std::basic_string`属于C++ STL容器类，用户自定义的类也可以作为它的模板参数，因此也适用C++ STL Algorithm库。

`string`本质上是以字符作为元素的vector特化版本；不存在0字符结尾这个概念，能装入'\0'这种数据。

## std::basic_string类模板

`std::basic_string`类模板存储且操纵类似char的对象的序列。该对象类型的性质由特性类模板std::char_traits的实例来提供，并作为`std::basic_string`的第二个模板参数。

C++11标准规定：basic_string的元素是连续存储的。即对于basic_string s，有：`&*(s.begin() + n) == &*s.begin() + n`，其中n属于0, s.size)。换句话说，指向s0的指针即为指向CharT数组的首元素指针。C++11已经禁止了写入时复制（copy-on-write)的实现，因为存在多线程安全问题。一般都采用了小字符串优化（SSO）实现，如Visual C++：

```cpp
union _Bxty
    {   // storage for small buffer or pointer to larger one
    _Elem _Buf[_BUF_SIZE];
    _Elem *_Ptr;
    } _Bx; 
size_type _Mysize;  // current length of string
size_type _Myres;   // current storage reserved for string
```

GCC从版本5开始，std::string不再采用COW策略。

C++17标准规定，basic_string是AllocatorAwareContainer, SequenceContainer与ContiguousContainer。

### 模板参数

- CharT - 字符类型
- Traits - 字符的特性类
- Allocator 内部存储的分配器类

### 成员类型

- traits_type 模板参数Traits
- value_type 即Traits::char_type
- allocator_type模板参数Allocator
- size_type 即Allocator::size_type。C++11改为std::allocator_traits::size_type
- difference_type即Allocator::difference_type。C++11改为std::allocator_traits::difference_type
- reference 即Allocator::reference。C++11改为value_type&
- const_reference 即Allocator::const_reference。C++11改为const value_type&
- pointer 即Allocator::pointer。C++11改为std::allocator_traits::pointer
- const_pointer 即Allocator::const_pointer。C++11改为std::allocator_traits::const_pointer
- iterator 属于RandomAccessIterator
- const_iterator 属于Constant random access iterator
- reverse_iterator 即std::reverse_iterator
- const_reverse_iterator 即std::reverse_iterator

### 成员函数

下面列出所有成员函数，其中 `string`是 `std::basic_string<T>` 的简写：

- 构造表示
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/basic_string string]`（构造）
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/~basic_string ~string]`（析构）
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/operator= operator=]` - 赋值
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/assign assign]` –赋值
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/get_allocator get_allocator]` –获得内存分配器
- 字符访问
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/at at]` –访问特定字符，带边界检查
- string:: operator`[]` –访问特定字符
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/front front]` –访问第一个字符
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/back back]` –访问最后一个字符
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/data data]` –访问基础数组，C++11 后与 c_str 完全相同
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/c_str c_str]` –返回对应于字符串内容的 C 风格零结尾的只读字符串
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/substr substr]` –以子串构造一个新串；参数为空时取全部源串
- 迭代器
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/begin begin]` –获得指向开始位置的迭代器
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/end end]` –获得指向末尾的迭代器
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/rbegin rbegin]` –获得指向末尾的逆向迭代器
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/rend rend]` –获得指向开始位置的逆向迭代器
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/cbegin cbegin]` –获得指向开始位置的只读迭代器
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/cend cend]` –获得指向末尾的只读迭代器
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/crbegin crbegin]` –获得指向末尾的逆向只读迭代器
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/crend crend]` –获得指向开始位置的逆向只读迭代器
- 容量
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/empty empty]` –检查是否为空
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/size size]` –返回数据的字符长度
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/length length]` –返回数据的字符长度，与 size 完全相同
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/max_size max_size]` –返回可存储的最大的字节容量，在 32 位 Windows 上大概为 43 亿字节。
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/reserve reserve]` –改变 string 的字符存储容量，实际获得的存储容量不小于 reserve 的参数值。
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/capacity capacity]` –返回当前的字符存储容量
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/shrink_to_fit shrink_to_fit]`（C++11 新增）–降低内存容量到刚好
- 修改器
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/clear clear]` –清空内容
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/insert insert]` –插入字符或字符串。目标 string 中的插入位置可用整数值或迭代器表示。如果参数仅为一个迭代器，则在其所指位置插入0 值。
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/erase erase]` –删除 1 个或 1 段字符
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/push_back push_back]` –追加 1 个字符
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/pop_back pop_back]` –删除最后 1 个字符，C++11 标准引入
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/append append]` –追加字符或字符串
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/operator+= operator+=]` –追加，只有一个参数——字符指针、字符或字符串；不像 append 一样可以追加参数的子串或若干相同字符
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/copy copy]` –拷贝出一段字符到 C 风格字符数组；有溢出危险
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/resize resize]` –改变（增加或减少）字符串长度；如果增加了字符串长度，新字符缺省为 0 值
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/swap swap]` –与另一个 string 交换内容
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/replace replace]` –替换子串；如果替换源数据与被替换数据的长度不等，则结果字符串的长度发生改变
- 搜索
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/find find]` –前向搜索特定子串的第一次出现
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/rfind rfind]` –从尾部开始，后向搜索特定子串的第一次出现
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/find_first_of find_first_of]` –搜索指定字符集合中任意字符在 *this 中的第一次出现
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/find_last_of find_last_of]` –搜索指定字符集合中任意字符在 *this 中的最后一次出现
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/find_first_not_of find_first_not_of]` –*this 中的不属于指定字符集合的首个字符
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/find_last_not_of find_last_not_of]` –*this 中的不属于指定字符集合的末个字符
- `string::[http://en.cppreference.com/w/cpp/string/basic_string/compare compare]` –与参数字符串比较

### 常量值

- `string::[http://en.cppreference.com/w/cpp/string/basic_string/npos npos]` –表示“未找到”，值为`static const unsigned -1`

### 非成员的有关的全局函数

- `std::[http://en.cppreference.com/w/cpp/string/basic_string/operator+ operator+]` –字符串连接
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/operator_cmp operator!=]` –不等比较
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/operator_cmp operator==]` –相等比较
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/operator_cmp operator<]` –小于比较
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/operator_cmp operator<=]` –小于等于比较
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/operator_cmp operator>]` –大于比较
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/operator_cmp operator>=]` –大于等于比较
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/operator_ltltgtgt operator<<]` –字符串内容写到输出流中
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/operator_ltltgtgt operator>>]` –从输入流中读取一个字符串
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/getline getline]` –从istream中读入一行或一段字符到string中
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/swap2 swap]` –交换两个string的内容。是std::swap算法针对std::basic_string的特化版本
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/stol stoi]` –字符串转为整形
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/stol stol]` –字符串转为长整形
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/stol stoll]` –字符串转为长长整形
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/stoul stoul]` –字符串转为无符号长整形
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/stoull stoull]` –字符串转为无符号长长整形
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/stof stof]` –字符串转为单精度浮点形
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/stod stod]` –字符串转为双精度浮点形
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/stold stold]` –字符串转为长双精度浮点形
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/to_string to_string]` –整型、无符号整型、浮点型转化为string
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/to_wstring to_wstring]` –整型、无符号整型、浮点型转化为wstring
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/hash hash<std::string>]` –计算hash值
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/hash hash<std::wstring>]` –计算hash值
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/hash hash<std::u16string>]` –计算hash值
- `std::[http://en.cppreference.com/w/cpp/string/basic_string/hash hash<std::u32string>]` –计算hash值

### 字面量

C++14标准定义了如下的std::basic_string字面量：

- string operator "" s（const char *str, std::size_t len）;
- u16string operator "" s（const char16_t *str, std::size_t len）;
- u32string operator "" s（const char32_t *str, std::size_t len）;
- wstring operator "" s（const wchar_t *str, std::size_t len）;

示例：

```cpp
#include <string>
#include <iostream> 
int main()
{
    using namespace std::string_literals;
 
    std::string s2 = "abc\0\0def"; // forms the string "abc"
    std::string s1 = "abc\0\0def"s; // form the string "abc\0\0def"
    std::cout<<s1.size()<<std::endl; //output 8
    std::cout<<s2<<std::endl;
    std::cout<<s1<<std::endl;
}
```

### 构造hash值的函数

C++11标准引入了4个std::hash函数模板的特化。用于以string为键值的hash定址。

- template<> struct hash;
- template<> struct hash;
- template<> struct hash;
- template<> struct hash;

## std::char_traits类

char_traits是一个traits类模板。用于抽象出给定字符类型的字符特性与字符串操作。char_traits用于明确（explicit）实例化一个`std::basic_string`类模板。

- 成员类型
- char_type CharT
- int_type 可以保持char_type以及EOF的值的整数类型
- off_type 实现定义
- pos_type 实现定义
- state_type 实现定义
- 成员函数
- assignstatic赋值一个字符
- eqstatic 比较两个字符相等
- ltstatic 比较两个字符小于
- movestatic 移动一个字符序列到另一个字符序列
- copystatic 复制一个字符序列
- comparestatic词典序比较两个字符序列
- lengthstatic返回一个字符序列的长度
- findstatic 在一个字符序列中找到一个字符
- to_char_typestatic转化整型值到相等的char_type
- to_int_typestatic 转化char_type到相等的整型值
- eq_int_typestatic 比较两个整型值
- eofstatic 返回eof值
- not_eofstatic检查一个字符是否为eof值

例如，如果需要定义“两个字符相等”当且仅当“两个字符的大写形式相等”，就可以在std::char_traits之上派生定义一个类，重载定义eq、lt、compare、find四个静态成员函数。再用此特性类作为第二个模板参数去实例化std::basic_string类模板。

## C++11放弃了COW

从C++11开始，明确禁止用“写时复制”（Copy On Write）实现stl::string。因为这在并发时容易引发错误。如下例的注释：

```cpp
#include <string>
int main()
{
    std::string str1("hello world\n");

    //p指向str1的数据区
    const char * p = str1.data();

    {
        //str2和str1共享数据
        auto str2( str1);

        //访问导致str1复制数据，此时p和str2直线同一块区域
        str1[0];

        //str2离开作用域，调用析构函数，此时str2的RefCount为0，因此str2指向的内存被释放
    }

    //此时p为野指针，这是严重的bug
    printf(p);

    return 0;
}
```

## 小对象优化

现在的主流编译器与标准库基本都采用了小对象优化（small objects optimization，又叫 small buffer optimization），以节约动态分配内存开销。

### MSVC实现

MSVC不靠ptr指针判断是否启用SSO，而是通过 capacity 字段区分：

- 若 capacity == BUFSIZE - 1：使用内部缓冲区。
- 若 capacity >= BUFSIZE：使用外部堆缓冲区 ³。

MSVC同样选用 16 字节作为内嵌缓冲区总大小，结构采用联合体复用指针与数组内存。在release版中，小字符串缓冲区和存储区指针复用为一个union，再加上数据长度(_Mysize)、数据区总长度(_Myres)，共计24个字节（32位非宽字符）或32个字节（64位非宽字符）；在debug中，小字符串缓冲区和存储区指针各有自己的存储空间，因为共计28个字节（32位非宽字符）或40个字节（64位非宽字符）。

```cpp
template<typename T>
struct basic_string
{
    static constexpr size_t BUFSIZE = 16 / sizeof(T);
    union {
        T* ptr;          //_Ptr
        T buf[BUFSIZE];  //_Buf
    } storage;
    size_t size; // _Mysize
    size_t capacity; // _Myres
};
```

### GCC >= 5实现

早年 GCC 标准库采用引用计数 + 写时复制 (COW) 字符串实现，但 C++11 标准新增约束（c_str、data、operator 操作后迭代器不能失效），COW 方案不再合规，彻底废弃。

一旦启用内部缓冲区buf，capacity字段就不再需要单独存储 —— 内嵌缓冲区的最大容量是固定常量 BUFSIZE - 1。因此 GCC 用联合体复用内存，capacity 和内部数组共用同一块内存，二者互斥，不会同时使用。内嵌缓冲区总大小固定为 16 字节（减去 1 字节存空终止符）：

- char单字节字符，最多存15个有效字符。
- UTF-16双字节字符，最多存7个码元。

```cpp
template<typename T>
struct basic_string
{
    static constexpr size_t BUFSIZE = 16 / sizeof(T);
    T* ptr;
    size_t size;
    union {
        size_t capacity;
        T buf[BUFSIZE]; // 短字符串内嵌存储
    } storage;
};
```

### Clang libc++极致压缩实现

Clang 的优化最为激进，依靠对象首字节最低位标记区分长短字符串（小端系统下）：

- 首字节最低位 = 0：长字符串，使用外部堆分配。
- 首字节最低位 = 1：短字符串，启用 SSO。

```cpp
template<typename T>
struct basic_string_large
{
    size_t capacity; // 容量永远向上取偶数，最低位恒为0，作为标记
    size_t size;
    T* ptr;
};

template<typename T>
struct basic_string_small
{
    unsigned char is_small : 1; // 短字符串固定置1
    unsigned char size : 7;     // 剩余7bit存储字符串长度
    T buf[BUFSIZE];
};
```

64 位系统下：

- char（单字节）：缓冲区可存 22 个字符。
- wchar_t（双字节）：缓冲区可存 10 个字符。

长度上限均小于 128，刚好能用 7bit 存下长度。

这套巧妙的位压缩方案，让 64 位系统下整个 std::string 对象仅占 24 字节，同时支持最长 22 字节短 char 串、10 字符短宽串走零堆分配 SSO。

## 用法

```cpp
#include <iostream>
#include <string>

int main()
{
    std::string foo = "fighters";
    std::string bar = "stool";

    // "!=" compares string contents for inequality, even though they are different objects.
    if(foo != bar)
    {
        std::cout << "The strings are different." << std::endl;
    }

    // Prints "stool fighters" by creating a temporary object, which is automatically freed.
    std::cout << bar + " " + foo << std::endl;

    return 0;
}

/*
 Output:
 The strings are different.
 stool fighters
*/
```

由于字符串的拷贝操作与其字节长度成比例，是O（n）量级。且创建字符串的临时栈对象的成本开销。因此`string`一般作为常量引用（reference-to-const）以避免不必要的拷贝：

```cpp
void print_the_string(const std::string& str)
{
    std::cout << str;
}
```

`c_str()`成员函数返回string类的C语言风格字符串（即ASCII-零串）的指针，用于C语言字符串的互操作。如果不需要零结尾的字符串，那么成员函数`data()`返回不一定是0结尾的字符串的内存地址。