相信很多同学都听说过句柄,也看过一些对句柄的解释。但是句柄到底是什么?他是怎么工作的,可能真正能回答出来的人不多,今天我们借助resip协议栈对句柄的实现来详细聊聊句柄。
首先作为句柄它最重要的功能就是能唯一指定一个内存对象,注意这里是“指定”,不是c++指针的“指向”。那句柄要怎么才能做到唯一指定一个内存对象呢?
想象一下,如果一个类A中有一个成员变量id(通过特定机制保证类A的多个变量拥有不等的id), 它用来唯一标识一个类A的对象。类似于学生的学号,可以唯一指定一个学生。然后我们在创建一个对象的管理类A_Manager, 在A_Manager中包含一个map成员变量。这个map成员变量的key是类A对象的id,value是类A对象的指针。那么只要知道类A对象的id我们就能够从管理类A_Manager的map中找到该对象。
如果我们在申明一个类handle,这个handle类需要用A_Manager类对象的引用和A对象的id来构造。那么我们是不是就可以使用handle类的对象去找到那个指定id的A类的对象了?
没错上面说的handle就是一个句柄,通过它我们可以找到指定id的类A的对象。
通过上面的介绍我们大概能总结如下:
1、句柄不一定是指针,只要通过一个类的对象能够找到指定的其他类对象,我们就可以称这个类为其他类的句柄;
2、句柄可以有无限多个,并且删除句柄,不会影响到句柄指定的对象;
3、能够被句柄指定的对象一定得具备一些属性,并且能够通过这些属性唯一确定对象。如上面介绍的类A就有一个id属性,通过id我们可以找到那个唯一的对象。
4、通过一种机制保证类的多个变量的属性拥有不等的值。
在resip协议栈中就是通过上面的思想来实现句柄类的。
class HandleManager;
class Handled
{
public:
typedef unsigned long Id; // make this a UInt64, fix the hash
enum { npos = 0 };
Handled(HandleManager& ham);
virtual ~Handled();
virtual EncodeStream& dump(EncodeStream& strm) const=0;
protected:
HandleManager& mHam;
Handled::Id mId;
};
template <class T>
class Handle
{
public:
Handle(HandleManager& ham, Handled::Id id) : mHam(&ham), mId(id)
{
}
Handle() : mHam(0), mId(0)
{
}
bool isValid() const
{
if (!mHam)
{
return false;
}
else
{
return mHam->isValidHandle(mId);
}
}
// throws if not found
T* get()
{
if (!mHam)
{
//assert(0);
throw HandleException("Reference to unitialized handle.", __FILE__, __LINE__);
}
return static_cast<T*>(mHam->getHandled(mId));
}
const T* get() const
{
if (!mHam)
{
//assert(0);
throw HandleException("Reference to unitialized handle.", __FILE__, __LINE__);
}
return static_cast<T*>(mHam->getHandled(mId));
}
T* operator->()
{
return get();
}
const T* operator->() const
{
return get();
}
T& operator*()
{
return *get();
}
const T& operator*() const
{
return *get();
}
Handled::Id getId() const
{
return mId;
}
static Handle<T> NotValid()
{
static Handle<T> notValid;
return notValid;
}
bool operator==(const Handle<T>& other)
{
return (mHam == other.mHam) && (mId == other.mId);
}
// !nash! to be able to use Handle in Set or Map container
bool operator<(const Handle<T>& other) const
{
assert(mHam);
assert(other.mHam);
return mId < other.mId;
}
private:
HandleManager* mHam;
protected:
Handled::Id mId;
friend class Handled;
};
class HandleManager
{
public:
HandleManager();
virtual ~HandleManager();
bool isValidHandle(Handled::Id) const;
Handled* getHandled(Handled::Id) const;
virtual void shutdownWhenEmpty();
//subclasses(for now DUM) overload this method to handle shutdown
protected:
virtual void onAllHandlesDestroyed()=0;
virtual void dumpHandles() const;
//private:
friend class Handled;
Handled::Id create(Handled* handled);
void remove(Handled::Id id);
typedef HashMap<Handled::Id, Handled*> HandleMap;
HandleMap mHandleMap;
bool mShuttingDown;
Handled::Id mLastId;
public:
/// Returns the number of handles in use.
HandleMap::size_type handleCount(void) const
{
return mHandleMap.size();
}
};
通过上面代码的分析,我们可以看出handle对象是handled对象的句柄,handled有成员变量mId来区分不同的handled。HandleManager类提供一个map用来储存handled对象。handled对象都是通过HandleManger类来创建,从而保证每个handled类的对象拥有不等的mId。最后handle对象需要通过HandleManager的引用与handled对象的id来创建,以确保handle类对象可以准确找到handled类的对象。