一、场景
你有大量对象共享同一份数据。大部分操作只是读数据,偶尔写。如果每次拷贝都深复制一份,浪费内存和 CPU。
COW 的思路:读操作共享同一份数据,写操作才复制。
class SharedBuffer {
struct Data {
std::vector<char> buf;
int ref_count = 1;
};
Data* data_;
public:
SharedBuffer() : data_(new Data()) {}
// 浅拷贝:共享同一个 Data
SharedBuffer(const SharedBuffer& other)
: data_(other.data_) {
data_->ref_count++;
}
~SharedBuffer() {
if (--data_->ref_count == 0) {
delete data_; // 最后一个引用才释放
}
}
// 读操作:共享,不需要复制
const char* read() const {
return data_->buf.data();
}
// 写操作:先检查引用计数,如果共享了就要复制
char* write() {
if (data_->ref_count > 1) {
// 有人在共享,先复制一份
Data* new_data = new Data(*data_);
new_data->ref_count = 1;
data_->ref_count--;
data_ = new_data;
}
return data_->buf.data();
}
};
二、面试追问 Q&A
Q1:std::string 为什么 C++11 后不再用 COW?
A:COW 在多线程下有严重问题。判断"是否共享"需要原子操作,且 operator[] 返回的引用可能被用作写操作,但你无法在返回引用时复制。C++11 起标准要求 operator[] 是 $O(1)$ 且不修改 this,所以 COW 被 SSO(Short String Optimization)替代了。
Q2:什么时候适合自己实现 COW? A:只读场景远多于写场景,且数据拷贝成本高的时候。比如编辑器文档模型、图像处理中的图层。