Appearance
std::string 详解
本篇把 STL 中关于字符串的考点补齐:string 存储模型、常用方法复杂度、手写 String,并在文末附 STL 分层总结与 tuple/variant/any 辨析(optional 见
optional.md)。
一、std::string
重点考点
- 存储模型(SSO 小字符串优化):多数实现(libstdc++/libc++/MSVC)对小字符串(通常 ≤15/22 字节)直接存在对象内部的 buffer 中,不堆分配;超过才用动态内存。这使小字符串拷贝/构造很快,是面试常问「string 是怎么存」的答案。
- 底层是一个
char*+ 长度/容量,与 vector<char> 类似,连续存储(C++11 保证c_str()/data()连续且以 '\0' 结尾)。 - 常见复杂度:
+/append/find的复杂度说明(find 通常 O(n))。 - 高频题:
- 手写 String(拷贝构造/赋值/析构/operator== /operator+);
findvsrfindvssubstr substr底层;reserve/resize与容量。
手写简易 String(老八股)
cpp
#include <cstring>
#include <utility>
class String {
public:
String() : buf_(nullptr), len_(0) {}
String(const char* s)
: len_(s ? std::strlen(s) : 0) {
buf_ = new char[len_ + 1];
std::memcpy(buf_, s ? s : "", len_ + 1);
}
// 拷贝构造
String(const String& o) : String(o.buf_) {}
// 拷贝赋值(copy-and-swap)
String& operator=(String o) {
std::swap(buf_, o.buf_);
std::swap(len_, o.len_);
return *this;
}
// 移动
String(String&& o) noexcept : buf_(o.buf_), len_(o.len_) {
o.buf_ = nullptr; o.len_ = 0;
}
~String() { delete[] buf_; }
size_t size() const { return len_; }
const char* c_str() const { return buf_ ? buf_ : ""; }
char& operator[](size_t i) { return buf_[i]; }
const char& operator[](size_t i) const { return buf_[i]; }
bool operator==(const String& o) const {
return len_ == o.len_ && std::memcmp(c_str(), o.c_str(), len_) == 0;
}
String& operator+=(const String& o) {
char* nb = new char[len_ + o.len_ + 1];
std::memcpy(nb, c_str(), len_);
std::memcpy(nb + len_, o.c_str(), o.len_);
nb[len_ + o.len_] = '\0';
delete[] buf_;
buf_ = nb; len_ += o.len_;
return *this;
}
private:
char* buf_;
size_t len_;
};要点:new char[len+1] 要多留 1 字节放 '\0';拷贝赋值传值 + swap 天然异常安全;记得 delete[]。
二、std::optional(C++17)
已单独成篇,详见
optional.md(含「为什么需要、底层实现、值访问 API、手写思路」)。
一句话记忆:optional = 一个可能没值的 T,底层就地存储 + has_value 标志,不堆分配。
std::nullopt表示空;value()无值抛bad_optional_access;value_or(fallback)给默认。- 不支持
optional<T&>。
三、std::tuple / std::variant / std::any(了解)
std::tuple(C++11):异构定长容器,std::get<i>、std::tie、结构化绑定(C++17)。底层类似「成员依次排列」+ 空基类优化(EBCO)。std::variant(C++17):类型安全的联合体(sum type),同一时刻只存一个类型,std::visit访问。无指针风险,比裸 union 安全。std::any(C++17):类型擦除的可存任意类型容器,底层常是虚函数表 + 堆对象。
三者区分
| 组件 | 语义 | 底层 |
|---|---|---|
| tuple | 同时存多个不同类型的值 | 嵌套结构体 |
| variant | 同一时刻存「其中一种」类型 | 判别式联合 |
| any | 存「任意一种」类型(运行期才知道) | 类型擦除 + 虚表 |
| optional | 存「有或没有」一个 T(详见 optional.md) | 值 + bool |
四、STL 分层总结记忆卡
- 序列容器选型:
- 大多时候 → vector
- 频繁中间插入删、不需要随机 → list
- 频繁头尾操作 → deque
- 有序字典 → map(红黑树 O(logn),键有序)
- 无序字典/复杂键查字典 → unordered_map(哈希 O(1),需 hash+==)
- 去重/集合 → set / unordered_set
- 优先级 → priority_queue(堆)
- 可变长字符串 → string
- 键不可重复 / 需要多个值 → multimap / 或用
map<K, vector<V>>
面试追问「为什么不用 / 改用」,多基于以上底层差异作答。