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。賦值函數的邏輯同理,由於智能指針指向的新的對象,就會增加新對象的引用計數,而減少舊對象的引用計數。