C++新特性16_写时拷贝
接上篇内容遗留了两个问题,第一个问题及解决方法如下:
1. 问题:如果共享资源中的值发生了变化,那么其他使用该共享资源的值如何保持不变?
下图中将"li si"改为"li si2"后,两个对象指向的内容都同时发生改变。
2. 解决思路:使用引用计数时,当发生共享资源值改变的时候,需要对其资源进行重新的拷贝,这样改变的是拷贝的值,而不影响原有的对象中的共享资源。
写时拷贝(COW copy on write),在所有需要改变值的地方,重新分配内存。
涉及资源的拷贝和引用计数的加减(原引用计数减1,新的引用计数变为1)
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
struct RefValue
{
RefValue(const char* pszName);
~RefValue();
void AddRef();
void Release();
char* m_pszName;
int m_nCount;//利用普通的变量作为计数器
};
RefValue::RefValue(const char* pszName)
{
m_pszName = new char[256];
strcpy(m_pszName, pszName);
m_nCount = 1;
}
RefValue::~RefValue()
{
if (m_pszName != NULL)
{
delete m_pszName;
m_pszName = NULL;
}
}
//计数器加1函数
void RefValue::AddRef()
{
m_nCount++;
}
void RefValue::Release()
{
if (--m_nCount == 0)
{
delete this;
}
}
class CStudent
{
public:
CStudent(const char* pszName);
CStudent(CStudent& obj);
void SetName(const char* pszName);
~CStudent();
CStudent& operator=(CStudent& obj); main中采用stu1 = stu2;这种形式时才会调用=运算符重载
void release();
void Show()
{
if (m_pValue->m_nCount > 0)
{
cout << hex << (int&)m_pValue->m_pszName << m_pValue->m_pszName << endl;
}
}
private:
RefValue* m_pValue;
};
void CStudent::SetName(const char* pszName)
{
m_pValue->Release();//原来的计数器处理
m_pValue = new RefValue(pszName);//创建一个新的计数器
}
CStudent::CStudent(const char* pszName)
{
m_pValue = new RefValue(pszName);
}
//构造的时候浅拷贝,计数器加1
CStudent::CStudent(CStudent& obj)
{
m_pValue = obj.m_pValue;
m_pValue->AddRef();
}
CStudent::~CStudent()
{
release();
}
//=运算符重载的时候浅拷贝,计数器加1
CStudent& CStudent::operator=(CStudent& obj)
{
if (obj.m_pValue == m_pValue)
{
return *this;
}
m_pValue->Release();
m_pValue = obj.m_pValue;
m_pValue->AddRef();
return *this;
}
void CStudent::release()
{
m_pValue->Release();
}
int main(int argc, char* argv[])
{
CStudent stu1("zhang san");
CStudent stu2("li si");
CStudent stu3 = stu2;
stu2.Show();
stu3.Show();
stu2.SetName("li si2");//释放与新建计数器
stu2.Show();
stu3.Show();
return 0;
}
上面程序中的核心就在于:
void CStudent::SetName(const char* pszName)
{
//原来的计数器处理,计数器减一后判断是否为0,是0则释放
m_pValue->Release();
m_pValue = new RefValue(pszName);//创建一个新的计数器,表示复制之后的对象
}
//计数器减一后判断是否为0,是0则释放
void RefValue::Release()
{
if (--m_nCount == 0)
{
delete this;
}
}
//创建一个新的计数器
RefValue::RefValue(const char* pszName)
{
m_pszName = new char[256];
strcpy(m_pszName, pszName);
m_nCount = 1;
}
程序运行之后就可以实现stud2改变资源之后,不会改变sud3的资源,实现写时拷贝。