这里实现弱引用的方法和boost还是有区别的,boost中的弱引用是依赖一个强引用,就是需要有一个智能指针,而这里实现的弱引用和强引用无关,只跟变量和引用对象有关系。不过,如果只靠这两个函数管理变量地址来表示弱引用,代码写起来实在是比较晦涩,即使保证了 add_weak_ref 和 release_weak_ref 成对使用,有时也很难分清一个变量是否变成弱引用了。所以,我们应该像高级语言一样,再封装出一个弱引用对象出来。这时模板就派上用场了,可以写一个弱引用模板把 add_weak_ref 和 release_weak_ref 封装起来。用户可以选择是使用模板还是亲自使用变量。
[cpp] view plaincopy
template<typename T> struct TWeakRefPtr
{
private:
WeakRefObj* m;
public:
TWeakRefPtr(): m(0) { };
TWeakRefPtr(const TWeakRefPtr& value): m(0)
{
if(value.m != 0)
value.m->add_weak_ref(&this->m);
};
TWeakRefPtr(T* ptr_t): m(0)
{
if(dynamic_cast<WeakRefObj*>(ptr_t) != 0)
static_cast<WeakRefObj*>(ptr_t)->add_weak_ref(&this->m);
};
~TWeakRefPtr()
{
if(this->m != 0)
this->m->release_weak_ref(&this->m);
};
operator T*()
{
return dynamic_cast<T*>(this->m);
};
operator const T*() const
{
return dynamic_cast<const T*>(this->m);
};
TWeakRefPtr& operator =(T* ptr_t)
{
if(this->m != 0)
this->m->release_weak_ref(&this->m);
if(dynamic_cast<WeakRefObj*>(ptr_t) != 0)
{
static_cast<WeakRefObj*>(ptr_t)->add_weak_ref(&this->m);
}
else
{
this->m = 0;
}
return (*this);
};
TWeakRefPtr& operator =(const TWeakRefPtr& value)
{
if(this->m != 0)
this->m->release_weak_ref(&this->m);
if(value.m != 0)
{
value.m->add_weak_ref(&this->m);
}
else
{
this->m = 0;
}
return (*this);
};
bool operator ==(const TWeakRefPtr& value) const
{
return (this->m == value.m);
};
bool operator ==(T* ptr_t) const
{
return (this->m == static_cast<WeakRefObj*>(ptr_t));
};
bool operator !=(const TWeakRefPtr& value) const
{
return (this->m != value.m);
};
bool operator !=(T* ptr_t) const
{
return (this->m != static_cast<WeakRefObj*>(ptr_t));
};
bool operator <(const TWeakRefPtr& value) const
{
return (this->m < value.m);
};
T* operator ->() const
{
return static_cast<T*>(this->m);
};
};
模板没法封装,必须在头文件里写完,虽然代码很多,不过关键功能都是靠 add_weak_ref 和 release_weak_ref 实现,这两个函数的实现代码是隐藏在cpp文件里的,也算是封装了。
如果考虑线程
安全,要做的事就多了,最简单的,
Windows平台可以用 CRITICAL_SECTION,WeakRefObj对象可以维护一个CRITICAL_SECTION,不仅使管理弱引用变得线程安全,还可以加两个成员函数:
sync_lock() 调用EnterCriticalSection,
sync_unlock() 调用LeaveCriticalSection,
这样顺便让整个对象甚至继承的对象都是线程安全的。
从系统设计上来看,弱引用使代码逻辑看起来比强引用更和谐。强引用管理对象可靠,但是不能克服循环引用的顽疾,弱引用对此是一个弥补。出现循环引用的根本原因是接口内部实现者之间发生了强引用,而这个强引用并不是使用者亲自增加的,所以使用者没把它当做一次引用。从系统设计上来说,应该是只有接口的使用者有权管理强引用,接口的内部实现者如果需要相互引用,应该全都是弱引用。当使用者通过强引用释放接口时,内部实现者通过检查弱引用是否失效作出正确的处理,就可以避免引用循环造成的悲剧。
按照这样的想法,接口的实现者最好既支持强引用又支持弱引用,因此这个WeakRefObj可以继续改进,最后成为一个通用的接口基类interface_type。由于包含了一个 void* _internal 成员变量,最终形成的类不算真正意义上的接口,不过 _internal 是私有且无明确类型,继承后也无法访问到,然后可以通过protected关键字隐藏构造函数和析构函数,使接口公开后既不被能构造也不能被析构,只能通过强引用(引用计数)管理接口的生存时期,同时支持弱引用,这样作为接口基类还勉强可以接受。其实通过重载 new 操作符返回偏移地址,或者像高级语言一样在某个堆空间里保存所有创建出的对象,这个 void* _internal 也是可以消除的,不过由此带来的多线程安全问题和性能下降都很严重,所以就保留现状了。如何去除这个唯一成员变量暂时没思路了。