句柄来源于这样的需求:想把一个继承层次的对象装在一个容器里,显然不能用基类的数组,也不能用基类的数组指针,因为用基类的数组指针无法解决容器中存在多个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


Logo

权威|前沿|技术|干货|国内首个API全生命周期开发者社区

更多推荐