【C++沉思录】句柄1

1、在【C++沉思录】代理类中,使用了代理类,存在问题:
a、代理复制,每次创建一个副本,这个开销有可能很大
b、有些对象不能轻易创建副本,比如文件
2、怎么解决这个问题?
使用引用计数句柄,对动态资源封装,句柄包含指针,多个句柄可以指向同一个对象。复制的时候,只是复制句柄的指针。
3、使用引用计数句柄,是为了避免不必要的对象复制,因此我们要知道有多少个句柄绑定到当前对象,也就是引用计数,
这样才能确定何时可以释放资源。
4、需要注意的是:引用计数不能是句柄的一部分,如果怎么做,当前句柄必须知道指向同一个对象的其他句柄,引用计数也要保持一致。
同时,引用计数不能成为对象的一部分,如果这样做,要求我们重写已经存在的对象类。
5、以Point类为例说明,解决办法是:增加一个新的类UPoint,包含Point对象和引用计数u,如下:
class Point
{
public:
Point():_x(0),_y(0){}
Point(int x,int y):_x(x),_y(y){}
Point(const Point& rhs):_x(rhs._x),_y(rhs._y){}

Point& SetX(int x)
{
_x = x;
return *this;
}

int GetX()
{
return _x;
}

Point& SetY(int y)
{
_y = y;
return *this;
}

int GetY()
{
return _y;
}

private:
int _x;
int _y;
};

#include "point.h"

// UPoint的目的是对Point和引用计数封装,用户是不可见的
class UPoint
{
friend class Handle_1;

private:
Point p;
int u;

UPoint():u(1){}

UPoint(int x,int y)
{
p.SetX(x);
p.SetY(y);
u = 1;
}

UPoint(const Point& rhs)
{
p = rhs;
u = 1;
}
};

6、现在考虑Handle_1的实现细节,
#include "u_point.h"

class Handle_1
{
public:
Handle_1():_up(new UPoint){}

Handle_1(int x,int y):_up(new UPoint(x,y)){}

Handle_1(const Point& rhs):_up(new UPoint(rhs)){}

~Handle_1()
{
subRef();
}

// copy构造,复制指针,增加引用
Handle_1(const Handle_1& rhs)
{
addRef(rhs._up);
}

// copy赋值,左边减少引用计数,判断是否delete,右边增加引用计数,考虑自我赋值
Handle_1& operator=(const Handle_1& rhs)
{
if(this != &rhs)
{
subRef();
addRef(rhs._up);
}

return * this;
}


int GetX()
{
return _up->p.GetX();
}

int GetY()
{
return _up->p.GetY();
}

Handle_1& SetX(int x)
{
_up->p.SetX(x);
return *this;
}

Handle_1& SetY(int y)
{
_up->p.SetY(y);
return *this;
}

private:
void addRef(UPoint* up) // 复制指针,增加引用
{
_up = up;
++_up->u;
}

void subRef() // 减少引用,判断是否delete
{
if(--_up->u == 0)
{
delete _up;
}
}

private:
UPoint* _up;
};

7、考虑下面的情况,
Handle_1 h1(3,4);
Handle_1 h2(h1);
h2.SetX(5);
int dd = h1.GetX();
dd的值是5,也就是说,多个句柄指向同一个对象,避免了不必要的对象复制,实现的是 指针语义。但是对于上面的情况,往往不是用户所期望的,
怎么解决这个问题?
8、使用写时拷贝,每次修改的时候重新创建一个对象。也就是说,修改的时候变成值语义,原对象h1是不可变对象,使用h2修改,会导致重新创建一个对象。
如下:
Handle_1& SetX(int x)
{
//_up->p.SetX(x);
if(_up->u == 1) // 当前是唯一的引用
{
_up->p.SetX(x);
}
else
{
--_up->u;
_up = new UPoint(x,_up->p.GetY());
}
return *this;
}

Handle_1& SetY(int y)
{
//_up->p.SetY(y);
if(_up->u == 1) // 当前是唯一的引用
{
_up->p.SetY(y);
}
else
{
--_up->u;
_up = new UPoint(_up->p.GetX(),y);
}
return *this;
}

原文地址:https://www.cnblogs.com/nzbbody/p/4678653.html