webrtc中的引用计数系统主要由四个类一起构建:
RefCounter,
RefCountInterface,
RefCountedObject,
scoped_refptr
1. RefCounter:
既然是引用计数系统,那就一定需要一个计数器,用于保存并更新引用计数。
webrtc中使用了C++的原子变量(std::atomic< int>),使用 fetch_add(1) 和fetch_sub(1) 做+1和-1操作。
RefCounter类的源码如下:
class RefCounter {
public:
explicit RefCounter(int ref_count) : ref_count_(ref_count) {}
RefCounter() = delete;
void IncRef() {
ref_count_.fetch_add(1, std::memory_order_relaxed);
}
rtc::RefCountReleaseStatus DecRef() {
int ref_count_after_subtract =
ref_count_.fetch_sub(1, std::memory_order_acq_rel) - 1;
return ref_count_after_subtract == 0
? rtc::RefCountReleaseStatus::kDroppedLastRef
: rtc::RefCountReleaseStatus::kOtherRefsRemained;
}
bool HasOneRef() const {
return ref_count_.load(std::memory_order_acquire) == 1;
}
private:
std::atomic<int> ref_count_;
};
2. RefCountInterface:
RefCountInterface是一个抽象接口类,提供AddRef()和Release()两个接口, 供需要使用引用计数的类来继承。
注意:webrtc中一个类如果要使用引用计数系统,则必须继承RefCountInterface(同时,这样会使得继承后的子类也成为一个抽象类,无法实例化))。
RefCountInterface类的源码如下:
class RefCountInterface {
public:
virtual void AddRef() const = 0;
virtual RefCountReleaseStatus Release() const = 0;
protected:
virtual ~RefCountInterface() {}
};
3. RefCountedObject:
RefCountedObject类真正实现了引用计数增减方法(实现了AddRef()和Release()函数),它有下面几个特性:
- 持有一个RefCounter成员;
- 实现AddRef()和Release()函数,通过调用RefCounter成员的IncRef()和DecRef()接口完成更新引用计数的动作;
- 是一个模板类,同时又继承模板参数,是模板参数的子类;
- 使用std::forward完美转发提高效率。
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);
};
小结: 实现一个带引用计数的类
至此,有了上面的 RefCounter、RefCountInterface、RefCountedObject 三个类,我们就可以实现一个带引用计数的类,例如 VideoTrack类:
// 通过继承RefCounterInterface基类,VideoTrack中带有AddRef()和Release()虚函数
class VideoTrack : public RefCounterInterface {
public:
VideoTrack(int id) : track_id(id) {}
private:
int track_id;
};
int id = 5;
// 使用VideoTrack实例化RefCountedObject模板类
RefCountedObject<VideoTrack> *instance = new RefCountedObject<VideoTrack>(id);
// RefCountedObject<VideoTrack>对象带有引用计数,支持手动的增减计数操作:
instance->AddRef();
instance->Release();
继承关系如下: RefCountedObject --> VideoTrack --> RefCounterInterface
关键点:
Q: 为什么不把AddRef()和Release()接口的实现直接放在VideoTrack类中,而要使用RefCountedObject这样一个奇怪的模板类作为VideoTrack的子类呢?
A: 因为如果没有RefCountedObject这个模板类,那么每一个想要使用引用计数系统的类都必须自行实现AddRef()和Release()接口,例如后面再有AudioTrack、PictureTrack等等类似的类,都要重复开发这两个接口函数。
这会带来很多重复代码,降低开发效率,并提高使用难度。
有了RefCountedObject这个模板类,只需要实现VideoTrack自身的业务函数,然后在需要创建VideoTrack实例时,按照格式 new RefCountedObject<VideoTrack>() 创建对象即可。
4. scoped_refptr:
scoped_refptr类的作用是真正实现 “智能指针”,在前面的RefCountedObject类型对象只是一个带有引用计数的类,为了避免直接对RefCountedObject对象做手动增减操作(这样做并不“智能”,可能会忘记调用AddRef()或Release()而引发内存泄漏,或重复Release()导致coredump),需要使用 scopted_refptr 来根据类的构造和析构动作,自动做增减操作。
scoped_refptr的使用方式如下:
scoped_refptr<VideoTrack> track = new RefCountedObject<VideoTrack>(id);
scoped_refptr的源码如下:
template <class T>
class scoped_refptr {
public:
typedef T element_type;
// 1. 6个构造函数
// 1.1 默认构造,传入空的引用计数对象
scoped_refptr() : ptr_(nullptr) {}
// 1.2 构造,传入引用计数对象p的指针,调用其AddRef()方法,引用计数+1。
scoped_refptr(T* p) : ptr_(p) { // NOLINT(runtime/explicit)
if (ptr_)
ptr_->AddRef();
}
// 1.3 拷贝构造,对应的引用计数+1,调用引用计数对象的AddRef()方法。
scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
if (ptr_)
ptr_->AddRef();
}
// 1.4 拷贝构造,U必须是T的子类,同1.3
template <typename U>
scoped_refptr(const scoped_refptr<U>& r) : ptr_(r.get()) {
if (ptr_)
ptr_->AddRef();
}
// 1.5 移动构造(右值引用),表示对象的转移,亦即使用同一个对象,因此,需要保持
// 对引用计数对象的引用次数不变。所以,此处调用scoped_refptr<T>的release()
// 方法,将原来的scoped_refptr<T>对象内部引用计数指针置空,新的scoped_refptr<T>
// 对象来保存引用计数对象,以达到转移的目的。
scoped_refptr(scoped_refptr<T>&& r) noexcept : ptr_(r.release()) {}
// 1.6 移动构造,U必须是T的子类,同1.5
template <typename U>
scoped_refptr(scoped_refptr<U>&& r) noexcept : ptr_(r.release()) {}
// 2 析构函数,调用引用计数对象Release(),引用计数-1
~scoped_refptr() {
if (ptr_)
ptr_->Release();
}
// 3. get、release、()、->()方法
T* get() const { return ptr_; }
operator T*() const { return ptr_; }
T* operator->() const { return ptr_; }
T* release() {
T* retVal = ptr_;
ptr_ = nullptr;
return retVal;
}
// 4. 重载赋值运算符
// 4.1 赋值新的引用计数对象的指针,新引用计数对象的引用计数+1,原来的-1
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;
}
// 4.2 赋值智能指针,新引用计数对象的引用计数+1,原来的-1
scoped_refptr<T>& operator=(const scoped_refptr<T>& r) {
// 取出智能指针的内部引用计数的指针,利用4.1的功能实现赋值
return *this = r.ptr_;
}
// 4.3 赋值T的子类U的智能指针,新引用计数对象的引用计数+1,原来的-1
// 具体使用过程中,这个赋值方法用得最多。
template <typename U>
scoped_refptr<T>& operator=(const scoped_refptr<U>& r) {
// 使用get()方法取出智能指针的内部引用计数的指针,利用4.1的功能实现赋值
return *this = r.get();
}
// 4.4 移动赋值右值智能指针,新引用计数对象的引用计数不变,原来引用计数对象的引用计数不变
scoped_refptr<T>& operator=(scoped_refptr<T>&& r) noexcept {
// 使用移动语义std::move + 移动构造 + swap进行引用计数对象的地址交换
scoped_refptr<T>(std::move(r)).swap(*this);
return *this;
}
// 4.5 移动赋值T的子类U的右值智能指针,新引用计数对象的引用计数不变,原来引用计数对象的引用计数不变
template <typename U>
scoped_refptr<T>& operator=(scoped_refptr<U>&& r) noexcept {
// 使用移动语义std::move + 移动构造 + swap进行引用计数对象的地址交换
scoped_refptr<T>(std::move(r)).swap(*this);
return *this;
}
// 5 地址交换函数
// 5.1 引用计数对象的地址交换
void swap(T** pp) noexcept {
T* p = ptr_;
ptr_ = *pp;
*pp = p;
}
// 5.2 智能智能内部的引用计数对象的地址交换,利用5.1
void swap(scoped_refptr<T>& r) noexcept { swap(&r.ptr_); }
protected:
T* ptr_;
};
至此,有了上述四个类,就实现了webrtc中的引用计数系统。