文档目录

一、场景

你有大量对象共享同一份数据。大部分操作只是读数据,偶尔写。如果每次拷贝都深复制一份,浪费内存和 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:只读场景远多于写场景,且数据拷贝成本高的时候。比如编辑器文档模型、图像处理中的图层。