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結尾的字串的記憶體地址。