std::basic_string是 C++ 标准库中所有“字符串类”的底层模板。我们平时用的std::string、std::wstring、std::u16string、std::u32string等,本质上都是std::basic_string针对不同字符类型的别名。它定义在头文件<string>中。
1. 模板定义与常见别名
std::basic_string的模板声明大致如下:
cpp
template< class CharT, class Traits = std::char_traits<CharT>, class Allocator = std::allocator<CharT> > class basic_string;
三个模板参数:
| 参数 | 含义 | 默认值 |
|---|---|---|
CharT | 字符类型,如char、wchar_t、char16_t、char32_t、char8_t | 无 |
Traits | 字符特性类,定义字符比较、长度、赋值、查找等操作 | std::char_traits<CharT> |
Allocator | 内存分配器 | std::allocator<CharT> |
常见别名:
cpp
using string = std::basic_string<char>; using wstring = std::basic_string<wchar_t>; using u16string = std::basic_string<char16_t>; using u32string = std::basic_string<char32_t>; using u8string = std::basic_string<char8_t>; // C++20
所以:
cpp
std::string == std::basic_string<char> std::wstring == std::basic_string<wchar_t>
std::basic_string拥有值语义,自动管理内存,支持拷贝、移动、迭代器、比较、查找、拼接等操作。
2. 核心特性
动态长度:可以在运行时增长、缩短。
自动内存管理:内部通过
Allocator分配和释放内存。连续存储:C++11 起保证字符在内存中连续存放。
以空字符结尾:
c_str()和data()返回的缓冲区保证以CharT()结尾。小字符串优化 SSO:短字符串通常直接存放在对象内部,避免堆分配。具体阈值由实现决定。
模板化:同一套代码可支持多种字符类型。
可定制:通过
Traits和Allocator可以定制字符行为和内存分配策略。不处理 Unicode 语义:它只存储
CharT序列,不负责编码转换、字素簇、规范化等。
3. 常用成员类型
cpp
value_type // CharT traits_type // Traits allocator_type // Allocator size_type // 通常为 std::size_t,无符号 difference_type // 通常为 std::ptrdiff_t reference // CharT& const_reference // const CharT& pointer // CharT* const_pointer // const CharT* iterator const_iterator reverse_iterator const_reverse_iterator
其中size_type是无符号类型,和int比较时要小心。
4. 常用操作分类
4.1 构造与赋值
cpp
std::basic_string<char> s1; // 空 std::basic_string<char> s2 = "hello"; // 从 C 字符串 std::basic_string<char> s3(5, 'a'); // "aaaaa" std::basic_string<char> s4(s2); // 拷贝 std::basic_string<char> s5(s2, 1, 3); // 从下标 1 开始取 3 个字符 std::basic_string<char> s6(s2.begin(), s2.end());
赋值:
cpp
s1 = "new"; s1.assign("another"); s1 = s2;4.2 容量与大小
cpp
s.size(); // 字符个数 s.length(); // 同 size() s.empty(); // 是否为空 s.capacity(); // 当前容量 s.reserve(100); // 预分配至少 100 个字符空间 s.resize(10); // 改变大小 s.clear(); // 清空,size 变 0,capacity 通常不变 s.shrink_to_fit(); // 请求释放多余容量,非强制
4.3 元素访问
cpp
s[0]; // 不检查越界 s.at(0); // 检查越界,越界抛 std::out_of_range s.front(); // 第一个字符 s.back(); // 最后一个字符 s.data(); // 底层字符数组指针 s.c_str(); // 以空字符结尾的 const CharT*
4.4 修改
cpp
s += " world"; s.append("!!"); s.push_back('!'); s.pop_back(); s.insert(0, "Hi "); s.erase(0, 3); s.replace(0, 2, "Bye"); s.swap(other);4.5 查找与子串
cpp
auto pos = s.find("world"); // 找不到返回 basic_string::npos auto pos2 = s.rfind("o"); auto pos3 = s.find_first_of("abc"); auto pos4 = s.find_last_not_of("xyz"); std::basic_string<char> sub = s.substr(7, 5);4.6 比较
cpp
s1 == s2; s1 != s2; s1 < s2; // 字典序 s1.compare(s2); // <0, 0, >0
4.7 迭代器与算法
cpp
for (auto it = s.begin(); it != s.end(); ++it) { ... } for (char& c : s) { ... } std::sort(s.begin(), s.end()); std::reverse(s.begin(), s.end());4.8 输入输出
cpp
std::cin >> s; // 读到空白 std::getline(std::cin, s); // 读一整行 std::cout << s;
对宽字符:
cpp
std::wcin >> ws; std::getline(std::wcin, ws); std::wcout << ws;
4.9 与 C 字符串互操作
cpp
const char* p = s.c_str(); const char* q = s.data();
注意:字符串被修改后,之前的c_str()/data()指针可能失效。
5. Traits 与 Allocator 的作用
5.1 Traits
Traits定义字符的基本操作,例如:
char_type:字符类型int_type:整数类型eq、lt、compare:比较length:求长度assign、move、copy:赋值和复制find:查找
默认std::char_traits<CharT>提供标准行为。一般不需要自定义。
5.2 Allocator
Allocator决定如何分配和释放内存。默认std::allocator<CharT>使用new/delete。自定义分配器可以用于内存池、调试、嵌入式等场景。
6. 与std::string_view的关系
std::basic_string拥有并管理内存。std::basic_string_view(C++17)只是一个只读视图,不拥有内存,适合作为函数参数避免拷贝。
cpp
void print(std::string_view sv); // 不拷贝
但要注意string_view的生命周期,它不延长底层字符串的寿命。
7. 版本差异要点
C++11:移动语义、
data()非 const、u16string、u32string。C++17:
basic_string_view、连续存储保证、starts_with/ends_with等尚未加入,但data()非 const。C++20:
constexpr支持、starts_with、ends_with、contains、char8_t和u8string、std::erase/std::erase_if。C++23:更多
constexpr和范围支持。
#include <iostream> #include <string> #include <type_traits> #include <algorithm> int main() { // 1. basic_string<char> 就是 std::string using MyString = std::basic_string<char>; static_assert(std::is_same_v<MyString, std::string>); MyString s = "Hello"; s += ", basic_string!"; std::cout << "s = " << s << '\n'; std::cout << "size = " << s.size() << ", capacity = " << s.capacity() << '\n'; auto pos = s.find("basic"); if (pos != MyString::npos) { std::cout << "find(\"basic\") = " << pos << '\n'; } MyString sub = s.substr(7, 12); std::cout << "substr = " << sub << '\n'; s.replace(7, 12, "world"); std::cout << "replace = " << s << '\n'; // 2. 宽字符版本 std::basic_string<wchar_t> ws = L"Wide"; ws += L" string"; std::wcout << L"ws = " << ws << L'\n'; std::wcout << L"ws.size() = " << ws.size() << L'\n'; // 3. 显式写出模板参数 std::basic_string<char, std::char_traits<char>, std::allocator<char>> explicit_str = "explicit"; std::cout << "explicit_str = " << explicit_str << '\n'; return 0; }