Handle其实就是智能指针的上古版本,了解一点C++11的应该对shared_ptr非常熟悉,那么你就把Handle当做shared_ptr来理解就没有任何问题了。
不过OCCT的Handles是侵入式的实现,前面讲过Standard_Transient类提供了引用计数机制,这个就是为了实现Handle
定义Handle
在需要使用Handle的类中,通过宏DEFINE_STANDARD_HANDLE(class_name,ancestor_name)来定义这个类的Hanle,例如给TopoDS_TShape定义一个Handle:
此宏展开后代码如下,可以看到,次宏定义了一个Handle_TopoDS_TShape类,不过这个类是个空壳,其实现基本在基类opencascade::handle<TopoDS_TShape>
class Handle_TopoDS_TShape : public opencascade::handle<TopoDS_TShape>
{
public:
Handle_TopoDS_TShape() {}
Handle_TopoDS_TShape(opencascade::handle<TopoDS_TShape>&& theHandle) : opencascade::handle<TopoDS_TShape>(theHandle)
{
}
template <class T2, typename = typename std::enable_if <std::is_base_of <TopoDS_TShape,T2>::value>::type>
inline Handle_TopoDS_TShape(const opencascade::handle<T2>& theOther) : opencascade::handle<TopoDS_TShape>(theOther)
{
}
template <class T2, typename = typename std::enable_if <std::is_base_of <TopoDS_TShape,T2>::value>::type>
inline Handle_TopoDS_TShape(const T2* theOther) : opencascade::handle<TopoDS_TShape>(theOther)
{
}
template<typename T> inline Handle_TopoDS_TShape& operator=(T theOther)
{
opencascade::handle<TopoDS_TShape>::operator=(theOther);
return *this;
}
};
Handle的实现
opencascade::handle是一个模板类,该类实现了共享智能指针的基本功能
- BeginScope,EndScope用于更新引用计数,管理生命周期
- 拷贝,赋值,移动,逻辑运算等函数或运算符的实现
- DownCast类型转换,向上类型转换
namespace opencascade {
template <class T>
class handle
{
public:
typedef T element_type;
public:
handle () : entity(0) {}
handle (const T *thePtr) : entity(const_cast<T*>(thePtr))
{
BeginScope();
}
handle (const handle& theHandle) : entity(theHandle.entity)
{
BeginScope();
}
handle (handle&& theHandle) noexcept : entity(theHandle.entity)
{
theHandle.entity = 0;
}
~handle ()
{
EndScope();
}
void Nullify()
{
EndScope();
}
bool IsNull() const { return entity == 0; }
void reset (T* thePtr)
{
Assign (thePtr);
}
handle& operator= (const handle& theHandle)
{
Assign (theHandle.entity);
return *this;
}
handle& operator= (const T* thePtr)
{
Assign (const_cast<T*>(thePtr));
return *this;
}
handle& operator= (handle&& theHandle) noexcept
{
std::swap (this->entity, theHandle.entity);
return *this;
}
T* get() const { return static_cast<T*>(this->entity); }
T* operator-> () const { return static_cast<T*>(this->entity); }
T& operator* () const { return *get(); }
template <class T2>
bool operator== (const handle<T2>& theHandle) const
{
return get() == theHandle.get();
}
template <class T2>
bool operator== (const T2 *thePtr) const
{
return get() == thePtr;
}
template <class T2>
friend bool operator== (const T2 *left, const handle& right)
{
return left == right.get();
}
template <class T2>
bool operator!= (const handle<T2>& theHandle) const
{
return get() != theHandle.get();
}
template <class T2>
bool operator!= (const T2 *thePtr) const
{
return get() != thePtr;
}
template <class T2>
friend bool operator!= (const T2 *left, const handle& right)
{
return left != right.get();
}
template <class T2>
bool operator< (const handle<T2>& theHandle) const
{
return get() < theHandle.get();
}
template <class T2>
static typename opencascade::std::enable_if<is_base_but_not_same<T2, T>::value, handle>::type
DownCast (const handle<T2>& theObject)
{
return handle (dynamic_cast<T*>(const_cast<T2*>(theObject.get())));
}
template <class T2>
static typename opencascade::std::enable_if<is_base_but_not_same<T2, T>::value, handle>::type
DownCast (const T2* thePtr)
{
return handle (dynamic_cast<T*>(const_cast<T2*>(thePtr)));
}
template <class T2>
__declspec(deprecated("down-casting from object of the same or unrelated type is meaningless"))
static handle DownCast (const handle<T2>& theObject, typename opencascade::std::enable_if<!is_base_but_not_same<T2, T>::value, void*>::type = 0)
{
return handle (dynamic_cast<T*>(const_cast<T2*>(theObject.get())));
}
template <class T2>
__declspec(deprecated("down-casting from object of the same or unrelated type is meaningless"))
static handle DownCast (const T2* thePtr, typename opencascade::std::enable_if<!is_base_but_not_same<T2, T>::value, void*>::type = 0)
{
return handle (dynamic_cast<T*>(const_cast<T2*>(thePtr)));
}
explicit operator bool () const
{
return entity != nullptr;
}
template <class T2, typename = typename std::enable_if<is_base_but_not_same<T2, T>::value>::type>
operator const handle<T2>& () const
{
return reinterpret_cast<const handle<T2>&>(*this);
}
template <class T2, typename = typename std::enable_if<is_base_but_not_same<T2, T>::value>::type>
operator handle<T2>& ()
{
return reinterpret_cast<handle<T2>&>(*this);
}
private:
void Assign (Standard_Transient *thePtr)
{
if (thePtr == entity)
return;
EndScope();
entity = thePtr;
BeginScope();
}
void BeginScope()
{
if (entity != 0)
entity->IncrementRefCounter();
}
void EndScope()
{
if (entity != 0 && entity->DecrementRefCounter() == 0)
entity->Delete();
entity = 0;
}
template <class T2> friend class handle;
private:
Standard_Transient* entity;
};
}
至于为什么需要弄一个Handle_TopoDS_TShape空壳类而不是直接模板实现就可以(一般只能指针都是一个模板类即可),应该是历史包袱问题,毕竟上古时期,编译器可能都不太支持模板。
Handle的一些问题
循环引用
标准库外的野鸡版本共享智能指针基本上都会有头疼的循环依赖问题,毕竟C++也是到了21世纪20年代才通过引入一个weak_ptr才解决shared_ptr的循环引用问题,这也不是大问题,避免循环依赖,或者手动打断依赖即可
性能问题
- 避免频繁down cast
- 避免拷贝
线程安全
Handle不是线程安全的,所以,上锁吧?
类型转换
DownCast的实现是dynamic_cast,这个就有点半拉子了,上篇讲到Standard_Transient自己实现了RTTI,而dynamic_cast依托于C++自带的RTTI,相当于双重性能消耗。一般追求性能的系统,自己实现RTTI时,可以实现安全的Cast函数,并且禁用dynamic_cast。