写时拷贝核心是“共享+计数+检测”:仅拷贝构造或赋值时共享内存并增引用计数;修改前检测计数>1则深拷贝,否则直改;计数归零才释放内存;现代std::string弃用主因是多线程开销与SSO替代。

写时拷贝不是一上来就复制,而是等真正要改的时候才动手。它靠的是“共享+计数+检测”三步配合,核心目标是省空间、避冗余、提效率。
共享数据的前提是什么?
只有通过拷贝构造或赋值操作创建的对象,才会和原对象共享底层内存。比如:
- String s1("hello"); → 独占一份内存,引用计数为 1
- String s2 = s1; → 不复制字符串内容,只让 s2 指向同一块内存,引用计数变成 2
- String s3(s1); → 同样是浅拷贝,引用计数再 +1
注意:用字符指针或字面量初始化(如 String s4 = "world";)不会触发共享,那是独立构造。
什么操作会真正触发拷贝?
只要尝试修改字符串内容,且当前内存被多个对象共享(引用计数 > 1),就会立刻深拷贝出一份新内存供自己使用。典型操作包括:
- push_back()、append()、operator+=()
- operator[] 非 const 版本(如 s[0] = 'H';)
- replace()、erase()、insert()
关键点在于:检测发生在“写入前一刻”。系统先检查 ref_count > 1,满足就分配新内存、复制内容、再执行修改;否则直接改原内存。
引用计数怎么管理?
计数器通常和数据内存一起动态分配,所有共享对象共用同一个整型指针。每次拷贝构造或赋值时加一,析构时减一。当减到 0,才真正释放那块内存和计数器本身。
这种设计保证了资源生命周期由“谁还在用”来决定,而不是谁先创建。
为什么现代标准库很少用它了?
C++11 之后,std::string 大多放弃 COW,主要因为:
- 多线程下引用计数增减需原子操作,带来性能开销
- operator[] 的 const/non-const 分离变得复杂,影响接口语义
- 短字符串优化(SSO)已能覆盖多数小字符串场景,COW 的收益变小
但理解它仍有价值——它是操作系统 fork、Qt 的 QString、早期 STL 实现等背后的关键逻辑。

















