句柄来源于这样的需求:想把一个继承层次的对象装在一个容器里,显然不能用基类的数组,也不能用基类的数组指针,因为用基类的数组指针无法解决容器中存在多个identical对象的情况【多个指针指向同一个对象,那么删除这个对象后指针指向何处呢?或者这些和对象关联的指针都被指向别的地方了,那么这个对象的内存就泄露了】,而且未初始化的指针是很危险的,因此要考虑封装指针,使其成为不容易出错又好用的智能指针。解决问题的办法是用surrogate或者handle。
本节介绍handle【引用和计数分离的思想】,首先有一个全局的计数类,用来统计指向同一个对象的handle的数目,复制handle对象将不会复制基础对象。
在handle对象中封装基类指针和全局计数类指针,基类指针用以指向派生类的内存单元,计数类指针用以指向全局计数类。然后令handle对象具有和要指向的对象具有相同的行为(类似DP中的adapter模式),即通过句柄来转发其指向对象的操作
Class Handle
{
Public:
Handle():p(0),use(new std::size_t(1)){}//默认的构造函数,没有绑定任何对象, 然后分配一个新的计数器并将它初始化为1
Handle(const Item_base&) //构造函数,将传入的参数(用户自己创建对象)绑定到句柄,并在这些对象上关联句柄。
Handle(const Handle&i):p(i.p),use(i.use){++*use;} //构造函数,用其他句柄来初始化
~Handle() {decr_use();}// 析构函数递减引用计数,如果计数为0就释放对象的内存。
Handle& operator=(const Handle& rhs)// 复制句柄时,rhs指向对象的引用计数增加,而this 句柄指向的对象的引用计数减少
{
++*rhs.puse; //增加rhs指向的那个对象的引用计数
decr_use(); //减少this句柄指向的哪个对象的引用计数,因为现在句柄需要指向一个新的对象,原有指向的对象的引用计数应该减一。
p=rhs.p; // this句柄指向rhs指向的那个对象,在两个对象发生分歧时再clone
puse=rhs.puse; //因为this句柄不再指向原先的对象A,因此也不能保存A对象的引用计数*puse,而要指向B对象的引用计数
return *this;
}
const Item_base* operator->() const // 转发句柄指向对象的操作handle->dosomething() 等价于ptr->dosomething()
{
if(p) return p;
else throw std::logic_error("unbound Sales_item");
}
const Item_base& operator*() const // 返回指向的对象
{
if(p) return *p;
else throw std::logic_error("unbound Sales_item");
}
private:
Item_base *p; //存储基类的指针
std::size_t *puse;// 引用计数类的指针,指向全局的计数类,size_t其实就是int,但是不同平台实现不同,因此定义此类型为了统一各个平台。
void decr_use()
{
if(--*puse==0){delete p;delete puse;}
}
};//end of class