侧边栏壁纸
博主头像
colo

欲买桂花同载酒

  • 累计撰写 1823 篇文章
  • 累计收到 0 条评论

实现一个支持写时复制(Copy-On-Write)的安全字符串类

2025-12-14 / 0 评论 / 0 阅读

题目

实现一个支持写时复制(Copy-On-Write)的安全字符串类

信息

  • 类型:问答
  • 难度:⭐⭐⭐

考点

指针与引用,内存管理,拷贝控制,移动语义,多线程安全

快速回答

实现要点:

  • 使用智能指针管理引用计数和共享数据
  • 实现深拷贝构造函数移动构造函数
  • 写操作前检查引用计数执行写时复制
  • 操作符重载保证边界安全
  • 使用std::atomic保证线程安全
## 解析

原理说明

写时复制(COW)通过共享数据减少拷贝开销,仅在修改时创建副本。核心挑战:

  1. 引用计数管理共享内存
  2. 写操作前的深拷贝触发条件
  3. 线程安全的引用计数更新
  4. 避免悬垂指针和内存泄漏

代码实现

#include <memory>
#include <atomic>
#include <cstring>

class CowString {
    struct Data {
        std::atomic<int> ref_count;
        char* buffer;
        size_t length;

        Data(const char* str, size_t len) 
            : ref_count(1), length(len) {
            buffer = new char[len + 1];
            memcpy(buffer, str, len);
            buffer[len] = '\0';
        }

        ~Data() { delete[] buffer; }
    };

    std::shared_ptr<Data> data;

    // 写前复制检查
    void detach() {
        if (data && data->ref_count > 1) {
            Data* new_data = new Data(data->buffer, data->length);
            data = std::shared_ptr<Data>(new_data);
        }
    }

public:
    // 构造函数
    explicit CowString(const char* str = "") {
        size_t len = strlen(str);
        data = std::make_shared<Data>(str, len);
    }

    // 拷贝构造函数(共享数据)
    CowString(const CowString& other) noexcept 
        : data(other.data) {
        if (data) data->ref_count++;
    }

    // 移动构造函数(转移所有权)
    CowString(CowString&& other) noexcept 
        : data(std::move(other.data)) {}

    // 写操作示例:修改字符
    char& operator[](size_t index) {
        if (index >= data->length) throw std::out_of_range("Index out of range");
        detach();  // 触发COW
        return data->buffer[index];
    }

    // 读操作示例:不触发复制
    const char& operator[](size_t index) const {
        if (index >= data->length) throw std::out_of_range("Index out of range");
        return data->buffer[index];
    }

    // 赋值运算符
    CowString& operator=(const CowString& other) {
        if (this != &other) {
            data = other.data;
            if (data) data->ref_count++;
        }
        return *this;
    }

    const char* c_str() const { 
        return data ? data->buffer : ""; 
    }
};

最佳实践

  • 智能指针管理:使用std::shared_ptr自动处理引用计数和内存释放
  • 写时复制优化detach()方法在修改前检查引用计数
  • 线程安全std::atomic保证引用计数操作的原子性
  • 异常安全:构造函数中分配内存失败会自动抛出异常

常见错误

错误类型后果解决方案
未实现COW无谓的深拷贝降低性能写操作前检查引用计数
缺少边界检查缓冲区溢出漏洞操作符重载中添加范围验证
非原子引用计数多线程下计数错误使用std::atomic
忽略自赋值问题资源提前释放赋值运算符检查this != &other

扩展知识

  • 短字符串优化(SSO):小型字符串直接存储在对象内部避免堆分配
  • 多读少写场景:COW在并发读取时性能优势明显,但频繁写入会降低性能
  • C++11移动语义:移动构造函数通过转移所有权避免COW开销
  • 性能权衡:现代CPU中COW可能因原子操作开销劣于直接拷贝,需根据场景选择