在 C++ 标准模板库中,string 类无疑是最基础却最重要的组件之一。无论你是准备技术面试,还是从事后端开发工作,对 string 的深入理解都不可或缺。本文将从实战应用、底层架构、模拟实现到高频 OJ 题目,带你系统性地掌握 string 的方方面面。
一、为什么 string 是 C++ 开发者的必修课?
很多从 C 语言转向 C++ 的开发者,初期都会困惑:既然已经有了 char*,为什么还需要 string?答案其实很直接——C 风格字符串存在太多痛点。
- 内存需要手动管理,稍不注意就会造成越界访问或内存泄漏
- str 系列函数与字符串本身分离,不符合面向对象的设计理念
- 拼接、查找、扩容、比较等操作实现繁琐
以 结尾的字符数组为例,每次操作都需要开发者自行确保内存安全。而在实际工作中,无论是 Python 的字符串处理,还是 Go 语言的 string 类型,都提供了更高级的抽象。C++ 的 string 类正是为了弥补这一差距而设计。\0
现实需求:OJ 平台上的字符串题目默认使用 string;企业级开发几乎不再使用 C 风格字符串;面试中深浅拷贝、扩容机制、底层结构都是高频考点。
二、string 核心接口与遍历技巧
使用 string 之前,需要引入头文件:
#include <string>
using namespace std;
C++11 引入的 auto 类型推导和范围 for 循环极大简化了 string 的遍历操作。auto 在编译期自动推导类型,必须初始化,不能作为函数参数或定义数组。
auto a = 10; // int
auto p = &a; // int*
auto& r = a; // int&
范围 for 的底层实现就是迭代器,能够自动遍历、自动结束,是 C++11 中最简洁的遍历方式:
string s = "hello";
for (auto& ch : s) { // 加 & 可修改
ch = toupper(ch);
}
for (auto ch : s) { // 只读
cout << ch << " ";
}
在构造函数方面,有四个重点需要掌握:
string s1; // 空串
string s2("hello"); // C 字符串构造
string s3(5, 'a'); // n 个字符
string s4(s2); // 拷贝构造
容量操作是面试必考内容,下面这张表值得反复记忆:
| 接口 | 作用 |
|---|---|
| size() / length() | 有效字符长度 |
| capacity() | 底层容量 |
| empty() | 是否为空 |
| clear() | 清空有效字符,不缩容 |
| reserve(n) | 预分配空间,不改 size |
| resize(n, c) | 改有效长度,多出来用 c 填充 |
⚠️ 一句话区分:reserve 只扩容,不改变长度;resize 会改变长度,并初始化新元素。
遍历 string 有三种主流方式。下标访问通过 实现:[]
for (int i = 0; i < s.size(); ++i)
cout << s[i];
迭代器方式更加通用:
string::iterator it = s.begin();
while (it != s.end()) {
cout << *it;
++it;
}
范围 for 则是 C++11 后的首选方案:
for (auto ch : s) cout << ch;
修改与查找是日常开发中最常用的操作:
s += "hello"; // 拼接(优先用)
s += ' ';
s.append("world");
const char* cstr = s.c_str(); // 转 C 字符串
size_t pos = s.find('w'); // 查找
string sub = s.substr(0,5); // 截取
当需要读取包含空格的整行输入时,getline 是最佳选择:
string line;
getline(cin, line);
[AFFILIATE_SLOT_1]
三、底层结构揭秘:SSO 与 COW 的较量
不同编译器对 string 的底层实现差异很大,这也是面试中的高频考点。
VS 编译器采用 SSO(短字符串优化):在 32 位环境下占用 28 字节。当字符串长度小于 16 时,直接存储在内部栈数组中;长度达到或超过 16 时,才转向堆空间。其结构由联合体、size、capacity 和指针组成。
GCC 编译器则采用 COW(写时拷贝):仅占 4 字节(一个指针)。堆内存中存储长度、容量和引用计数。读取时共享内存,写入时才真正复制,实现了高效的内存利用。
对比 Java 的 String 不可变设计和 Python 的字符串驻留机制,C++ 的这两种方案各有取舍。SSO 避免了小字符串的堆分配开销,COW 则在大字符串共享场景下优势明显。
四、深浅拷贝:面试必问的核心问题
浅拷贝是默认拷贝构造的行为——只复制指针,导致多个对象共用同一块内存。析构时会发生重复释放,直接导致程序崩溃。
深拷贝的解决方案是每个对象独立开辟空间,资源不共享,安全且不冲突。现代写法更加优雅:
String(const String& s) : _str(nullptr) {
String tmp(s._str);
swap(_str, tmp._str);
}
String& operator=(String s) {
swap(_str, s._str);
return *this;
}
写时拷贝(COW)则是浅拷贝加引用计数的组合:读操作共享,写操作才真正开空间。这种"拖延症式"的优化策略在特定场景下效率极高。
五、手写模拟实现与 OJ 实战
面试中经常要求手写 string 的核心实现,掌握以下框架可以直接应对:
class String {
public:
String(const char* str = "") {
if (str == nullptr) str = "";
_str = new char[strlen(str) + 1];
strcpy(_str, str);
}
// 深拷贝现代写法
String(const String& s) : _str(nullptr) {
String tmp(s._str);
swap(_str, tmp._str);
}
String& operator=(String s) {
swap(_str, s._str);
return *this;
}
~String() {
delete[] _str;
_str = nullptr;
}
const char* c_str() const { return _str; }
size_t size() const { return strlen(_str); }
char& operator[](sslocal://flow/file_open?url=size_t+i&flow_extra=eyJsaW5rX3R5cGUiOiJjb2RlX2ludGVycHJldGVyIn0=) { return _str[i]; }
private:
char* _str;
};
下面通过三道高频 OJ 题目巩固理解。第一题是仅仅反转字母:
string reverseOnlyLetters(string s) {
int l = 0, r = s.size()-1;
while (l < r) {
if (!isalpha(s[l])) l++;
else if (!isalpha(s[r])) r--;
else swap(s[l++], s[r--]);
}
return s;
}
第二题是第一个只出现一次的字符:
int firstUniqChar(string s) {
int cnt[256] = {0};
for (auto ch : s) cnt[ch]++;
for (int i = 0; i < s.size(); ++i)
if (cnt[s[i]] == 1) return i;
return -1;
}
第三题是经典的字符串相加(大数加法):
string addStrings(string num1, string num2) {
int i = num1.size()-1, j = num2.size()-1;
string res;
int carry = 0;
while (i >= 0 || j >= 0 || carry) {
int sum = carry;
if (i >= 0) sum += num1[i--] - '0';
if (j >= 0) sum += num2[j--] - '0';
carry = sum / 10;
res += (sum % 10) + '0';
}
reverse(res.begin(), res.end());
return res;
}
[AFFILIATE_SLOT_2]
六、核心要点总结
回顾全文,string 学习的核心可以归纳为以下几点:
- string 是管理字符数组的类,彻底替代了 C 风格字符串
- 优先使用
、+=、size()、reservec_str() - 遍历三法:
、迭代器、范围 for[] - resize 改长度,reserve 改容量,两者切勿混淆
- 底层实现:VS 用 SSO,GCC 用 COW
- 面试核心:浅拷贝/深拷贝、模拟实现、扩容机制
- OJ 高频:反转、去重、字符串加法、回文判断
掌握这些要点,无论是应对 C++ 面试还是日常工程开发,都能游刃有余。建议结合 JavaScript、Python 等语言的字符串处理方式对比学习,加深对 C++ string 设计哲学的理解。
浙公网安备 33010602011771号