弱引用是相对于强引用而言,它引用一个对象但是又不控制对象的生存时期,然后在使用时需要先检查引用的对象是否还存在。而强引用,一般是基于引用计数,引用计数明确的控制了对象的生存时期。如果按控制关系排一个顺序,就是:强引用控制对象生存时期,对象生存时期控制弱引用是否有效,弱引用则什么也没控制,它本身就是一个对象(例如C#里的System.WeakReference),高级一点可以是个模板。
弱引用貌似是各种高级语言中的神器,不过只要想费一点功夫,C++语言也可以实现,本文实现的版本是一个模板。弱引用实现的关键在于,当对象被删除时,需要及时的改变弱引用的状态,这需要引用的对象本身实现这些功能。因此为了实现弱引用,必须规定一个支持弱引用的基类,然后继承它的类都可以支持弱引用。这个类设计出来大概是这个样子:
h文件代码
struct WeakRefObj
{
private:
void* _internal;
protected:
WeakRefObj();
public:
~WeakRefObj();
void add_weak_ref(WeakRefObj** ref_ptr);
void release_weak_ref(WeakRefObj** ref_ptr, const bool clear_ref_ptr = false);
};
既然是基类,总是希望尽可能的简洁,尤其希望极少甚至没有成员变量声明在头文件里。不过研究了很久,总是不能避免添加成员变量,为了不让头文件暴漏太多内容,只声明了一个void* _internal,实际内容在实现代码中才能取得。
弱引用的关键功能通过 add_weak_ref 和 release_weak_ref 实现,add_weak_ref 是用来记下一个WeakRefObj* 变量的地址,通过记录变量地址 WeakRefObj**,这个变量就变成形式上的弱引用了。release_weak_ref 是把一个WeakRefObj* 变量的地址从记录中删除,使它还原为普通的变量。还有一个关键的函数,析构函数,在析构函数里要对已经记录的变量进行清零,这样当对象被删除后,所有被当做弱引用的变量都变成空值。在高级语言中,这个操作可能是垃圾回收器在某个时刻延时完成的,但在C++里必须在析构时全部清零。这样,弱引用的所有功能就算实现了。
不用多说,这里面有一大堆多线程同步问题要解决,不过先不考虑这些,暂时允许代码不是线程安全的。下面的实现过程中,记录的变量地址需要由 WeakRefObj 本身来保存,最简单的就是用std::set<T> 容器,void* _internal 可以指向一个std::set<WeakRefObj**>。
cpp文件代码
#include"WeakRefObj.h"
#include<set>
WeakRefObj::WeakRefObj
{
this->_internal = new std::set<WeakRefObj**>();
}
WeakRefObj::~WeakRefObj
{
if(this->_internal != NULL)
{
std::set<WeakRefObj**>* weak_refs = (std::set<WeakRefObj**>*)this->_internal;
for(std::set<WeakRefObj**>::iterator i = weak_refs->begin(), i_end = weak_refs->end(); i != i_end; i++)
{
(*(*i)) = NULL;
}
delete weak_refs;
}
}
void WeakRefObj::add_weak_ref(WeakRefObj** ref_ptr)
{
std::set<WeakRefObj**>* weak_refs;
if((*ref_ptr) != NULL)
{
weak_refs = (std::set<WeakRefObj**>*)(*ref_ptr)->_internal;
weak_refs->erase(ref_ptr);
}
weak_refs = (std::set<WeakRefObj**>*)this->_internal;
weak_refs->insert(ref_ptr);
(*ref_ptr) = this;
}
void WeakRefObj::release_weak_ref(WeakRefObj** ref_ptr, const bool clear_ref_p