
std::shared_ptr 本身不实现 COW
标准库的 std::shared_ptr 是引用计数型智能指针,它共享的是指针所指向的对象,但**不自动对对象内容做写时复制**。也就是说,多个 std::shared_ptr 指向同一块内存时,任何一方修改对象内容,其他方立刻可见——这恰恰是“非 COW”行为。
若想实现 COW,必须在对象层面封装逻辑,而不是依赖 std::shared_ptr 自身。
手动实现 COW 容器(以字符串为例)
常见做法是:内部用 std::shared_ptr 管理一个带引用计数的缓冲区,读操作直接访问;写操作前先检查引用计数,若大于 1,则分配新缓冲区并复制数据,再修改。
关键点:
立即学习“C++免费学习笔记(深入)”;
-
std::shared_ptr仅管理底层数据块,不是用户可见对象 - COW 判断必须显式调用
use_count(),且需在写入前检查 - 必须确保所有写入口(如
operator[]、append())都走同一套复制逻辑 - 多线程下
use_count()不是原子的,需额外同步(如std::mutex)或改用std::atomic手动计数
class cow_string {
struct rep {
std::string data;
mutable std::mutex mtx; // 保护 use_count 和 data
mutable long refcount = 1;
};
std::shared_ptr p_;
public:
cowstring(const char* s) : p(std::makeshared()) {
p ->data = s;
}
char& operator[](size_t i) {
if (p_.use_count() youjiankuohaophpcn 1) { // 写前检查
auto new_p = std::make_sharedzuojiankuohaophpcnrepyoujiankuohaophpcn();
new_p-youjiankuohaophpcndata = p_-youjiankuohaophpcndata;
p_ = new_p;
}
return p_-youjiankuohaophpcndata[i];
}};
std::string 在 C++11 后已弃用 COW
早期 GCC libstdc++ 的 std::string 曾实现 COW,但因违反 C++11 标准中对迭代器/引用有效性与数据竞争的要求,被彻底移除。现在所有主流标准库(libstdc++、libc++、MSVC STL)的 std::string 都是“立即复制”或“小字符串优化(SSO)”,不再提供 COW 语义。
这意味着:
- 不要假设
std::string 有 COW 行为,哪怕测试看似成立
- 跨线程传递
std::string 时,不能靠 COW 规避拷贝——必须显式使用 std::string_view 或 const std::string&
- 若真需 COW,只能自己封装,且务必处理好线程安全
替代方案:用 std::string_view + 显式克隆
更现代、更轻量的做法是避免隐藏的 COW 开销,改用明确的视图+按需复制:
- 函数参数优先用
std::string_view 接收只读字符串
- 需要修改时,由调用方决定是否构造新
std::string(如 std::string{sv})
- 配合
std::shared_ptr 共享只读数据,写操作则创建新实例
这种模式把“何时复制”的决策权交给业务层,比隐式 COW 更可控,也更容易调试和测试。
真正难的不是复制逻辑本身,而是判断哪些场景值得引入 COW —— 大多数时候,清晰的值语义或视图语义比隐藏的延迟复制更可靠。











