题目
实现一个支持写时复制(Copy-On-Write)的安全字符串类
信息
- 类型:问答
- 难度:⭐⭐⭐
考点
指针与引用,内存管理,拷贝控制,移动语义,多线程安全
快速回答
实现要点:
- 使用智能指针管理引用计数和共享数据
- 实现
深拷贝构造函数和移动构造函数 - 写操作前检查引用计数执行
写时复制 - 操作符重载保证
边界安全 - 使用
std::atomic保证线程安全
原理说明
写时复制(COW)通过共享数据减少拷贝开销,仅在修改时创建副本。核心挑战:
- 引用计数管理共享内存
- 写操作前的深拷贝触发条件
- 线程安全的引用计数更新
- 避免悬垂指针和内存泄漏
代码实现
#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可能因原子操作开销劣于直接拷贝,需根据场景选择