std::shared_ptr不实现写时复制(COW),仅管理对象指针的引用计数;COW需在对象层面手动实现,且须处理线程安全与统一写入口逻辑。

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<long>手动计数
class cow_string {
struct rep {
std::string data;
mutable std::mutex mtx; // 保护 use_count 和 data
mutable long refcount = 1;
};
std::shared_ptr<rep> p_;
<p>public:
cow<em>string(const char* s) : p</em>(std::make<em>shared<rep>()) {
p</em>->data = s;
}</p><pre class='brush:php;toolbar:false;'>char& operator[](size_t i) {
if (p_.use_count() > 1) { // 写前检查
auto new_p = std::make_shared<rep>();
new_p->data = p_->data;
p_ = new_p;
}
return p_->data[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<const std::string>共享只读数据,写操作则创建新实例
这种模式把“何时复制”的决策权交给业务层,比隐式 COW 更可控,也更容易调试和测试。
真正难的不是复制逻辑本身,而是判断哪些场景值得引入 COW —— 大多数时候,清晰的值语义或视图语义比隐藏的延迟复制更可靠。











