C++ RAII对象
RAII的全称是Resource Acquisition Is Initialization,即“资源获取就是初始化”。RAII的做法是使用一个对象,在其构造时获取对应的资源,在对象生命期内控制对资源的访问,使之始终保持有效,最后在对象析构的时候,释放构造时获取的资源。把资源放到对象里面,便可依赖C++的构造函数和析构函数机制,确保对资源的持有和释放。
智能指针便是属于RAII的一种,智能指针是一个用于管理资源的对象,其使用引用计数技术。在智能指针对象构造时,增加它所引用对象的的引用计数,在其析构时,减少它所持有对象的引用计数。当引用对象的引用计数为0时,便释放引用对象。
引用计数存放在引用对象中,在智能指针对象构造时,需要将待引用对象传入到智能指针对象的构造函数中,在构造函数会对引用对象的引用计数加1,当智能指针对象析构时,就将其所引用对象的引用计数减1。
Android内置的智能指针包含三种:轻量级指针、强指针、弱指针。
轻量级指针
轻量级指针通过简单的引用计数技术来维护对象的生命周期,下文从代码角度分析下轻量级指针的实现原理。
LightRefBase 类
// code path: /system/core/include/utils/lightRefBase.h
template <class T>
class LightRefBase
{
public:
inline LightRefBase() : mCount(0) { }
inline void incStrong(__attribute__((unused)) const void* id) const {
mCount.fetch_add(1, std::memory_order_relaxed);
}
inline void decStrong(__attribute__((unused)) const void* id) const {
if (mCount.fetch_sub(1, std::memory_order_release) == 1) {
std::atomic_thread_fence(std::memory_order_acquire);
delete static_cast<const T*>(this);
}
}
//! DEBUGGING ONLY: Get current strong ref count.
inline int32_t getStrongCount() const {
return mCount.load(std::memory_order_relaxed);
}
typedef LightRefBase<T> basetype;
protected:
inline ~LightRefBase() { }
private:
friend class ReferenceMover;
inline static void renameRefs(size_t /*n*/, const ReferenceRenamer& /*renamer*/) { }
inline static void renameRefId(T* /*ref*/, const void* /*old_id*/ , const void* /*new_id*/) { }
private:
mutable std::atomic<int32_t> mCount; // 对象的引用计数
};
任何需要使用轻量级指针的类对象都必须要继承 LightRefBase 类。LightRefBase是一个模板类,其中模板参数T表示对象的实际类型,它必须是继承了 LightRefBase 类的。其成员变量mCount用来描述对象的的引用计数值,提供了两个public接口 incStrong 和 decStrong 来增加和减少对象的引用计数值。
PS:在成员函数decStrong中,当对象的引用计数值为1时,其减少之后就会变成0,就表示需要需要释放这个对象所占用的内存了。
轻量级指针的实现类 sp
我们有了轻量级指针所指向的对象,那么这个对象由谁来指向?即谁来调用对象的 incStrong 和 decStrong 函数?接下来需要真正的轻量级指针的实现类,Android提供的轻量级指针的实现类是 sp ,它也是强指针的实现类,这里仅关注它与轻量级指针相关的实现。
template<typename T>
class sp {
public:
inline sp() : m_ptr(nullptr) { }
sp(T* other); // NOLINT(implicit)
sp(const sp<T>& other);
sp(sp<T>&& other);
template<typename U> sp(U* other); // NOLINT(implicit)
template<typename U> sp(const sp<U>& other); // NOLINT(implicit)
template<typename U> sp(sp<U>&& other); // NOLINT(implicit)
~sp();
// Assignment
sp& operator = (T* other);
sp& operator = (const sp<T>& other);
sp& operator = (sp<T>&& other);
template<typename U> sp& operator = (const sp<U>& other);
template<typename U> sp& operator = (sp<U>&& other);
template<typename U> sp& operator = (U* other);
//! Special optimization for use by ProcessState (and nobody else).
void force_set(T* other);
// Reset
void clear();
// Accessors
inline T& operator* () const { return *m_ptr; }
inline T* operator-> () const { return m_ptr; }
inline T* get() const { return m_ptr; }
inline explicit operator bool () const { return m_ptr != nullptr; }
// Operators
COMPARE(==)
COMPARE(!=)
COMPARE(>)
COMPARE(<)
COMPARE(<=)
COMPARE(>=)
private:
template<typename Y> friend class sp;
template<typename Y> friend class wp;
void set_pointer(T* ptr);
T* m_ptr;
};
sp 类也是一个模板类,模板参数T表示所引用对象的实际类型,该类型必须继承自 LightRefBase 类。sp 也是强指针的实现类,这里仅关注其对于轻量级指针的实现:
T* m_prt; //该成员变量用于指向所引用的对象,该对象必须继承自 LightRefBase
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other) {
if (other)
other->incStrong(this);
}
template<typename T>
sp<T>::sp(const sp<T>& other)
: m_ptr(other.m_ptr) {
if (m_ptr)
m_ptr->incStrong(this);
}
template<typename T>
sp<T>& sp<T>::operator =(T* other) {
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
if (other) other->incStrong(this);
if (oldPtr) oldPtr->decStrong(this);
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
m_ptr = other;
return *this;
}
template<typename T>
sp<T>& sp<T>::operator =(const sp<T>& other) {
// Force m_ptr to be read twice, to heuristically check for data races.
T* oldPtr(*const_cast<T* volatile*>(&m_ptr));
T* otherPtr(other.m_ptr);
if (otherPtr) otherPtr->incStrong(this);
if (oldPtr) oldPtr->decStrong(this);
if (oldPtr != *const_cast<T* volatile*>(&m_ptr)) sp_report_race();
m_ptr = otherPtr;
return *this;
}
template<typename T>
sp<T>::~sp() {
if (m_ptr)
m_ptr->decStrong(this);
}
sp 类和轻量级相关的就是它的构造函数、赋值操作符以及、析构函数。构造函数和析构函数的逻辑非常简单,构造时将所引用对象的引用计数加1,析构时将所引用对象的引用计数减1,都是调用 LightRefBase 的接口。
它的赋值操作符的逻辑就会稍有点复杂,再次先回忆下智能指针的原理:当引用对象有智能指针指向它时,就会给它的引用计数加1;当指向了所引用对象的智能指针不在指向改对象时,它的引用计数就会减1。赋值函数的逻辑同理,由于智能指针指向的新的对象,就会增加新对象的引用计数,而减少旧对象的引用计数。