C++句柄类

一直不能理解句柄类究竟有什么作用,尽管看到很多C++书籍中对句柄的使用进行了介绍。直到最近碰到一个问题才让我对句柄类的使用有了较为深入地了解。

这个问题简单来说是这样:设想一个类封装了一种数据结构,当我们声明该类的一个指针对象来指向这种数据结构的一块数据时,有两种情况:

1. 对数据块进行读取操作,并不改变数据的值

2. 对数据块进行改写操作,但要保留原数据块(因为其他地方要使用)


对于第一种情况,我们只需要构造一个类的指针对象,将他指向这个数据块。

对于第二种情况,问题有些不同。首先,需要在类的构造函数中重新申请内存将数据块拷贝过来,然后进行改写。但问题在于什么时候去申请新的数据块我们并不清楚。根据“写时复制”的原则,只有在我们需要改写数据块的时候才应该去重新申请内存。倘若一直没有对数据块进行改写,所有类的指针对象可以共享这个数据块中的内容。

这种内存管理的思想被许多程序框架使用,例如Qt中的隐式共享,在内容有变动的情况下才对的数据结构做复制,否则仅做共享。看一个例子:

QString str1 = "one"; //str1指向"one"
QString str2 = str1; //str2没有改变"one",此时"one"被共享两次
此时,指向"one"的计数器的值为2。现在,倘若我们改变str2指向的内容

str2 = "two";  //改变str2的内容
str2指向了新的数据块,因此"one"的计数减为1,"two"计数变为1。此时在改变str1的值
str1 = str2;
此时,"two"的计数增加到2,"one"计数变为0。这种对内存管理的方式可以用一个新的类来表示,这个类就是句柄。

简单来说,句柄就将指向数据的指针和该数据共享次数封装起来,我们直接看一个例子,假设有一个坐标类Point,建立一个Handle类来管理Point

Point类很简单,有两个成员(x,y)

class Handle;
class Point
{
public:
	friend Handle;
	Point(int a=0, int b=0):x(a), y(b){};
	~Point();

	Point& operator =(const Point &rhs);

public:
	int x;
	int y;
};
Point::~Point()
{
}

Point& Point::operator =(const Point &rhs)
{
    x = rhs.x+1;
    y = rhs.y+1;
    return *this;
}
句柄类

class Handle
{
public:
	Handle();
	Handle(int x, int y);
	Handle(const Point &Data);
	~Handle();

	Handle &operator =(const Handle &rhs);

private:
	Point *data;
	int *num;             //表示*data被共享的次数

	void destroy();
};
Handle::Handle()
{
	num = new int(1);   
	data = new Point(); //缺省复制构造函数将Point初始化为远点坐标 
}

Handle::Handle(int x, int y)
{
	num = new int(1);
	data = new Point(x,y);
}

Handle::Handle(const Point &Data)
{
	num = new int(1);
	data = new Point(Data);
}

Handle::~Handle()
{
	destroy();
}

void Handle::destroy()
{
	if(--*num == 0)             //如果指向数据的指针只有一个,则直接delete
	{                           //因为如果改变data指向的数据,则指向这块数据的指针个数为0
		delete data;
		data = 0;
		delete num;
		num = 0;
	}
}

Handle &Handle::operator =(const Handle &rhs)
{
	(*rhs.num)++;              //首先将右操作数的计数器加1,
	destroy();               
	data = rhs.data;
	num = rhs.num;
	return *this;
}

int main(void)
{
	Handle h(1,1);
	Handle h1;
	Handle h2;
	std::cout<<"h.num = "<<*h.num<<"    h1.num = "<<*h1.num<<"    h2.num = "<<*h2.num<<std::endl; //三个对象引用计数都为1
	h1 = h;      //指向h.data的引用计数变为2
	std::cout<<"h.num = "<<*h.num<<"    h1.num = "<<*h1.num<<"    h2.num = "<<*h2.num<<std::endl; 
	h1 = h2;    //指向h2.data的引用计数变为2,h.num减少为1
	std::cout<<"h.num = "<<*h.num<<"    h1.num = "<<*h1.num<<"    h2.num = "<<*h2.num<<std::endl;
	return 0; 
}
打印结果

h.num = 1    h1.num = 1    h2.num = 1

h.num = 2    h1.num = 2    h2.num = 1

h.num = 1    h1.num = 2    h2.num = 2

这样就实现了写时复制和对指针的动态管理,节省了复制数据时的开销






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值