写时复制这种技术是非常常见的 比如linux里面的fork()函数 等等 我也来写一个简单的实现
优点:读时共享 在复制对象的时候非常快 而且节省内存
缺点:在多线程环境且多写多读的情况下 需要频繁的上锁 性能会非常低下 不如直接拷贝一份
上锁又分为两种
1.外部上锁
即 让使用者去上锁 而不是在函数内部上锁 这样的好处是使用者可以自由的上锁 但是会带来更大的编程难度 比如boost里的asio就需要外部上锁来保证线程安全
2.内部上锁
即 在函数内部上锁 肯定是能保证线程安全 但是这样的话 即使是在单线程环境下 你仍然要加锁 这样肯定是浪费了性能 所以我们一般不采用这种方式 内部上锁比如 智能指针的引用计数的实现便是内部上锁 程序员不需要考虑
类图如下所示
接下来让我们来看代码吧
#include<string>
#pragma warning(disable:4996)
class listring
{
public:
listring(const char* c="")
{
p_ref = new referenceControlBlock(c);
}
~listring()
{
p_ref->~referenceControlBlock();
}
listring(const listring& ref)
{
p_ref = ref.p_ref;
p_ref->ref_count_++;
}
char& operator[](int index)//写时复制
{
if (p_ref->ref_count_ > 1)//触发写时复制
{
p_ref->ref_count_--;
p_ref = new referenceControlBlock(p_ref->real_value_);
return p_ref->real_value_[index];
}
else//仅有一个引用计数 不存在触发写时复制
{
return p_ref->real_value_[index];
}
}
private:
//引用控制块 使用类内类来做引用控制块
struct referenceControlBlock
{
int ref_count_{};
char* real_value_{};
referenceControlBlock(const char* c)
{
real_value_=new char[std::strlen(c)+1];
strcpy(real_value_, c);
ref_count_++;
}
referenceControlBlock(const referenceControlBlock* ref)
{
ref_count_++;
real_value_ = ref->real_value_;
}
~referenceControlBlock()
{
ref_count_--;
if (ref_count_ == 0)
{
delete[] real_value_;
}
}
};
referenceControlBlock* p_ref;
};
使用方法如下
listring li("fuck this");
listring liu(li);
使用后内存示意图
当使用以下语句发生写时复制时
liu[0] = 'c';
内存示意图如下
至此我们完成了写时复制的简单实现
具体来说 如果是多读少写的情况下 使用这种方式是非常快的 比一开始就拷贝要快的多