引用计数功能在对象的回收中至关重要,java 中,每个对象都会有引用计数,当引用计数为 0 时,对象将被标记会可回收,等待着垃圾回收机制进行回收。C++中没有垃圾自动回收的机制,但是不妨碍我们想办法自己实现这种引用计数的功能。在 Webrtc 中,RefCountedObject 类就是承担了这样的功能。
template <class T>
class RefCountedObject : public T {
public:
RefCountedObject() {}
template <class P0>
explicit RefCountedObject(P0&& p0) : T(std::forward<P0>(p0)) {}
template <class P0, class P1, class... Args>
RefCountedObject(P0&& p0, P1&& p1, Args&&... args)
: T(std::forward<P0>(p0),
std::forward<P1>(p1),
std::forward<Args>(args)...) {}
virtual void AddRef() const { ref_count_.IncRef(); }
virtual RefCountReleaseStatus Release() const {
const auto status = ref_count_.DecRef();
if (status == RefCountReleaseStatus::kDroppedLastRef) {
delete this;
}
return status;
}
virtual bool HasOneRef() const { return ref_count_.HasOneRef(); }
protected:
virtual ~RefCountedObject() {}
mutable webrtc::webrtc_impl::RefCounter ref_count_{0};
RTC_DISALLOW_COPY_AND_ASSIGN(RefCountedObject);
};
代码比较简单,是一个模板类。我注意到这里有个比较高级的用法,RefCountedObject 类继承了 class T。也就是说 RefCountedObject<A>是 A 的一个子类,这样的好处是 A 的代码无需继承 RefCountedObject。但我不是特别喜欢这种代码样式,不符合我们一般人的思维方式。
类中有一个变量 ref_count_,里面封装了 long 型的变量,并且实现了线程安全的加 1 和减 1。
RefCountedObject 只是让类拥有了引用计数的功能,并没有自动回收的功能,或许你看到 Release 函数里有 delete this 的代码,觉得这个就是回收的功能。这样理解好像也没什么问题,但是类 A 中并没有调用 Release,它甚至不知道 RefCountedObject 的存在,那 Release 究竟是在哪被调用呢?因此要实现引用计数自动回收的功能,必须配合 scoped_refptr<>模板类进行使用。
scoped_refptr<A> sa = new RefCountedObject<A>();
这个语句看起来语法有点问题,等号左边是一个非指针对象 sa,等号右边却是一个 new 的指针对象。想做到这样其实也不难,重载一下 operator=就可以了。
scoped_refptr<T>& operator=(T* p) {
// AddRef first so that self assignment should work
if (p)
p->AddRef();
if (ptr_)
ptr_->Release();
ptr_ = p;
return *this;
}
可以看到,RefCountedObject 中实现的 AddRef 以及 Release 函数是在 scoped_refptr 中被调用的。关于 scoped_refptr,我会在另一篇文章中解析。