天天看點

Chromium和WebKit的智能指針實作原理分析

       C++不像Java一樣,由虛拟機負責對象配置設定和釋放。也就是說,開發人員使用C++編寫代碼時,要自己負責對象配置設定和釋放。WebKit和Chromium都是使用C++開發的,是以它們也面臨上述問題。在解決對象釋放問題時,要做到在對象不需要時自動釋放,因為手動釋放會帶來忘記釋放或者釋放後又繼續使用的隐患。智能指針是實作對象自動釋放的有效技術手段。本文就分析Chromium和WebKit的智能指針的實作。

老羅的新浪微網誌:http://weibo.com/shengyangluo,歡迎關注!

《Android系統源代碼情景分析》一書正在進擊的程式員網(http://0xcc0xcd.com)中連載,點選進入!

       在現實中,隻要是稍微複雜一點的C++程式,都是不可能不使用智能指針的,是以智能指針是C++程式的一個最基本的設施。例如,Android系統的Native Framework的各個子產品中,幾乎都可以看到sp和wp相關的代碼。從前面Android系統的智能指針(輕量級指針、強指針和弱指針)的實作原理分析一文可以知道,sp和wp就是Android系統提供的智能指針子產品類。其中,前者用來實作強引用,而後者用來實作弱引用。

       在廣義上,智能指針劃分為兩類。第一類智能指針引用的對象是可以共享的,也就是一個對象可以同時被多個智能指針引用。這類智能指針要求被引用的對象具有計數的功能,數值的大小就表示它目前被多少個智能指針引用。當一個對象的引用計數值等于0的時候,就表示它要被釋放了。這類智能指針适合作為函數參數或者傳回值在子產品之間進行傳遞,進而實作共享。第二類智能指針不要求被引用對象具有計數的功能。

       第二類智能指針引用的對象是獨占的,也就是一個對象同一時刻隻可以被一個智能指針引用。這類智能指針不要求被引用對象具有計數的功能。隻要這類智能指針的生命周期超出了它自己的範圍,那麼它引用的對象就會被自動銷毀。這類智能指針适合在函數或者内部使用,用來自動化釋放那些不需要了的對象。

       WebKit和Chromium都同時提供了上述兩種類型的智能指針的實作。此外,WebKit和Chromium還提供了弱智能指針。所謂弱智能指針,就是它們的存在不會影響到被引用對象的生命周期。它們适合用來解決對象之間存在循環引用時的釋放問題。這一點我們在前面Android系統的智能指針(輕量級指針、強指針和弱指針)的實作原理分析一文有描述,這裡就不再累述。

       接下來,我們先分析WebKit的智能指針的實作,接着再分析Chromium的智能指針的實作,最後總結它們與Android系統實作的智能指針的差別,這樣我們就能夠對智能指針的實作有更深刻的了解。

       WebKit的第一類智能指針由類RefPtr實作。由于它要求被引用對象具有計數功能,是以就提供了一個具有計數功能的基類RefCounted。當一個對象可以被類RefPtr描述的對象引用時,它就必須要從基類RefCounted繼承下來。

       RefCounted是一個模闆類,為了減少編譯時的代碼膨脹,RefCounted類是從另外一個非模闆類RefCountedBase繼承下來的。RefCountedBase類才是負責提供計數功能的基類,它的實作如下所示:

class WTF_EXPORT RefCountedBase {
public:
    void ref()
    {
        ......
        ++m_refCount;
    }

    ......

protected:
    RefCountedBase()
        : m_refCount(1)
          ......
    {
    }

    ......

    bool derefBase()
    {
        ......

        --m_refCount;
        if (!m_refCount) {
            ......
            return true;
        }
  
        ......

        return false;
    }

    ......

private:
    ......

    int m_refCount;

    ......
};
           

       這個類定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/RefCounted.h中。 

       從這裡我們就可以看到,RefCountedBase類有一個類型為int的成員變量m_refCount,它就是用來描述對象的引用計數的。此外,RefCountedBase類還提供了兩個成員函數ref和derefBase,分别用來增加和減少成員變量m_refCount描述的對象引用計數。

       調用RefCountedBase類的成員函數derefBase減少一個對象的引用計數時,如果減少後的引用計數等于0,那麼它的傳回值就等于true,表示該對象應該被釋放了,不過這個釋放的操作留給子類RefCounted實作。

       從RefCountedBase類的構造函數可以看到,一個RefCountedBase類及其子類對象被建立出來的時候,它的引用計數值就已經被初始化為1,這意味着不需要額外調用RefCountedBase類的成員函數ref來增加新建立對象的引用計數。

       RefCounted類的實作如下所示:

template<typename T> class RefCounted : public RefCountedBase {
    ......

public:
    void deref()
    {
        if (derefBase())
            delete static_cast<T*>(this);
    }

    ......
};
           

       這個類定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/RefCounted.h中。

       RefCounted類有一個成員函數deref,它調用父類RefCountedBase類的成員函數derefBase減少對象的引用計數。從上面的分析可以知道,當RefCountedBase類的成員函數derefBase的傳回值等于true的時候,就表示對象該釋放了,是以RefCounted類的成員函數deref就直接将它delete掉。

       細心的同學會注意, RefCounted的基類RefCountedBase在增加或者減少引用計數的時候,并沒有加鎖或者執行原子加減操作。是以,我們就說RefCounted類是線程不安全的。如果我們需要線程安全版本的RefCounted類,WebKit提供了另外一個類ThreadSafeRefCounted,它繼承于ThreadSafeRefCountedBase類。

       ThreadSafeRefCountedBase類的實作如下所示:

class WTF_EXPORT ThreadSafeRefCountedBase {
    ......
public:
    ......

    void ref()
    {
        atomicIncrement(&m_refCount);
    }

    ......

protected:
    ......

    bool derefBase()
    {
        ......
        if (atomicDecrement(&m_refCount) <= 0) {
            ......
            return true;
        }
        return false;
    }

private:
    int m_refCount;
};
           

       這個函數定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/ThreadSafeRefCounted.h。

       與RefCountedBase類一樣,ThreadSafeRefCountedBase類也實作了成員函數ref和derefBase,不過它通過原子操作atomicIncrement和atomicDecrement來分别增加和減少目标對象的引用計數,是以它是線程安全的。

       ThreadSafeRefCounted類的實作如下所示:

template<class T> class ThreadSafeRefCounted : public ThreadSafeRefCountedBase {
public:
    void deref()
    {
        if (derefBase())
            delete static_cast<T*>(this);
    }

    ......
};
           

       這個函數定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/ThreadSafeRefCounted.h。

       與RefCounted類一樣,ThreadSafeRefCounted類也實作了成員函數deref,并且調用父類ThreadSafeRefCountedBase的成員函數derefBase減少對象的引用計數。當對象的引用計數小于等于0時,就會将它delete掉。

       有了RefCounted和ThreadSafeRefCounted這兩個基類為對象提供計數功能之後,我們就繼續分析第一類智能指針RefPtr的實作,如下所示:

namespace WTF {

    ......

    template<typename T> class RefPtr {
        ......
    public:
        .......
        ALWAYS_INLINE RefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); }
        ......
        ALWAYS_INLINE explicit RefPtr(T& ref) : m_ptr(&ref) { m_ptr->ref(); }
        ALWAYS_INLINE RefPtr(const RefPtr& o) : m_ptr(o.m_ptr) { refIfNotNull(m_ptr); }

        ......

        ALWAYS_INLINE ~RefPtr() { derefIfNotNull(m_ptr); }

        ALWAYS_INLINE T* get() const { return m_ptr; }

        ......

        T& operator*() const { return *m_ptr; }
        ALWAYS_INLINE T* operator->() const { return m_ptr; }
 
        ......

    private:
        T* m_ptr;
    };

    ......
};
           

       這個函數定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/RefPtr.h中。

       注意,這裡的模闆參數T要麼是從RefCounted的子類,要麼是ThreadSafeRefCounted的子類。

       除了參數類型為T&的構造函數之外,RefPtr類的所有其它構造函數都會調用函數refIfNotNull來增加目标對象的引用計數。在參數類型為T&的構造函數中,能夠保證參數ref不為NULL,是以就可以直接調用目标對象的成員函數ref來增加其引用計數。

       相應地,RefPtr類的析構函數會調用函數derefIfNotNull來減少目标對象的引用計數。這樣就可以保證一個智能指針超出其生命周期時,能夠自動釋放它引用的對象。

       為了能夠通過智能指針來調用目标對象的成員函數,RefPtr類實作了兩個操作符重載成員函數*和->。此外,RefPtr類還實作了一個成員函數get,用來擷取一個指向目标對象的裸指針,這樣就實作智能指針到裸指針的轉換。

       函數refIfNotNull和derefIfNotNull的實作如下所示:

namespace WTF {
    ......

    template<typename T> ALWAYS_INLINE void refIfNotNull(T* ptr)
    {
        if (LIKELY(ptr != 0)) {
            ......
            ptr->ref();
        }
    }

    template<typename T> ALWAYS_INLINE void derefIfNotNull(T* ptr)
    {
        if (LIKELY(ptr != 0))
            ptr->deref();
    }

    ......
}
           

       這兩個函數定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/PassRefPtr.h中。

       函數refIfNotNull和derefIfNotNull分别能過調用參數ptr描述的對象的成員函數ref和deref減少其引用計數。不過在增加和減少參數ptr描述的對象的引用計數之前,會先判斷參數ptr的值是否等于0。隻有在不等于0的情況下,才會執行增加和減少引用計數的操作。

       以上就是WebKit的第一類智能指針RefPtr的實作。在實際使用中,似乎沒有什麼問題。但是細心的同學可能會注意到,當智能指針作為參數或者傳回值在函數之間傳遞時,都會執行一次加1的引用計數操作,而當參數或者傳回值超出其生命周期時,又會對稱地執行一次減1的引用計數操作。這些加1和減1的引用計數操作是否是必須的呢?我們可以看下面的例子:

RefPtr<T2> foo(RefPtr<T1> p2) {
    RefPtr<T2> p3 = new T2();
    
    ......

    return p3;
}

RefPtr<T1> p1 = new T1();
RefPtr<T2> p4 = foo(p1);
           

       我們先new一個T1對象,并且通過它來構造智能指針p1時,這時候T1對象的引用計數為1,它不會被銷毀。接下來我們以智能指針p1為參數調用函數foo,這時候智能指針p2也引用了T1對象,是以T1對象的引用計數增加為2,它也不會被銷毀。從函數foo傳回後,智能指針p2超出了其生命周期範圍,是以T1對象的引用計數減少為1,它仍然是不會被銷毀。要等到智能指針p1也超出其生命周期範圍時,T1對象的引用計數才會減少為0,進而被釋放。

       在函數foo内部,我們先new一個T2對象,并且通過它來構造智能指針p3,這時候T2對象的引用計數為1,它不會被銷毀。接下來函數foo傳回智能指針p3給智能指針p4,這時候智能指針p4也引用了T2對象,是以T2對象的引用計數增加為2,它也不會被銷毀。與此同時,智能指針p3超出了其生命周期範圍,是以T2對象的引用計數減少為1,它仍然是不會被銷毀。同樣是要等到智能指針p4也超出其生命周期範圍時,T2對象的引用計數才會減少為0,進而被釋放。

       很明顯,在調用函數foo期間,完全是沒有增加和減少T1對象和T2對象的引用計數的,這相當于是做了兩次無用功。而且在實際的工程實踐中,上述的代碼是相當普通的,是以就會充斥着大量的無用功操作。

       為了去除上述的無用功,WebKit提供了另外一個智能指針類PassRefPtr,它的實作如下所示:

namespace WTF {
    ......

    template<typename T> class PassRefPtr {
        ......
    public:
        PassRefPtr() : m_ptr(0) { }
        PassRefPtr(std::nullptr_t) : m_ptr(0) { }
        PassRefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); }
        ......
        explicit PassRefPtr(T& ptr) : m_ptr(&ptr) { m_ptr->ref(); }

        ......
        PassRefPtr(const PassRefPtr& o) : m_ptr(o.leakRef()) { }
        ......

        ALWAYS_INLINE ~PassRefPtr() { derefIfNotNull(m_ptr); }

        template<typename U> PassRefPtr(const RefPtr<U>&, EnsurePtrConvertibleArgDecl(U, T));

        T* get() const { return m_ptr; }

        T* leakRef() const WARN_UNUSED_RETURN;

        T& operator*() const { return *m_ptr; }
        T* operator->() const { return m_ptr; }

        ......
    private:
        ......

        mutable T* m_ptr;
    };

    template<typename T> template<typename U> inline PassRefPtr<T>::PassRefPtr(const RefPtr<U>& o, EnsurePtrConvertibleArgDefn(U, T))
        : m_ptr(o.get())
    {
        T* ptr = m_ptr;
        refIfNotNull(ptr);
    }

    template<typename T> inline T* PassRefPtr<T>::leakRef() const
    {
        T* ptr = m_ptr;
        m_ptr = 0;
        return ptr;
    }

    ......
};
           

       這個類定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/PassRefPtr.h中。

       注意,這裡的模闆參數T同樣要麼是從RefCounted的子類,要麼是ThreadSafeRefCounted的子類。

       從PassRefPtr類的構造函數可以知道,當它們的參數是T*、T&和const RefPtr<T>&時,PassRefPtr類描述的智能指針與RefPtr類描述的智能指針并沒有什麼差別。但是當參數為const PassRefPtr<T>&時,也就是我們是一個PassRefPtr智能指針p1構造另一個PassRefPtr智能指針p2時,或者說将一個PassRefPtr智能指針p1傳遞給另一個PassRefPtr智能指針p2時,神奇的事情就發生了!智能指針p1就自動失去了對目标對象的引用,并且這時候目标對象的引用計數沒有發生變化。這是通過PassRefPtr類的成員函數leakRef實作的。

       有了PassRefPtr類之後,我們再來改寫上面舉的例子,如下所示:

PassRefPtr<T2> foo(PassRefPtr<T1> p2) {
    PassRefPtr<T2> p3 = new T2();
    
    ......

    return p3;
}

PassRefPtr<T1> p1 = new T1();
PassRefPtr<T2> p4 = foo(p1);
           

       這時候很明顯,T1對象和T2對象在函數foo的調用或者傳回前後,它們的引用計數值都始終保持為1,隻有當智能指針p1和智能指針p4超出其生命周期範圍時,T1對象和T2對象的引用計數才會減少為0,進而被釋放。這樣就可以免去智能指針作為參數或者傳回值傳遞目标對象的增加和減少引用計數操作,進而去掉無用功。

       從前面PassRefPtr類的定義可以知道,我們可以将一個RefPtr智能指針傳遞給一個PassRefPtr智能指針,這時候目标對象的引用計數會增加1。我們還可以另外一種方式将一個RefPtr智能指針傳遞給一個PassRefPtr智能指針。這種傳遞方式将會使得目标對象的引用計數不發生變化,并且RefPtr智能指針自動失去對目标對象的引用。這是通過調用RefPtr類的成員函數release實作的,如下所示:

namespace WTF {

    ......

    template<typename T> class RefPtr {
        ......
    public:
        ......

        PassRefPtr<T> release() { PassRefPtr<T> tmp = adoptRef(m_ptr); m_ptr = 0; return tmp; }
 
        ......

    private:
        T* m_ptr;
    };

    ......
};
           

       這個函數定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/RefPtr.h中。

       RefPtr類的成員函數release調用函數adoptRef來構造一個PassRefPtr智能指針,後者的實作如下所示:

namespace WTF {
    ......

    template<typename T> inline PassRefPtr<T> adoptRef(T* p)
    {
        ......
        return PassRefPtr<T>(p, PassRefPtr<T>::AdoptRef);
    }

    ......
};
           

       這個函數定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/PassRefPtr.h中。

       函數adoptRef調用以下的PassRefPtr類的構造函數來構造一個PassRefPtr智能指針,如下所示:

namespace WTF {
    ......

    template<typename T> class PassRefPtr {
        ......

    private:
        enum AdoptRefTag { AdoptRef };
        PassRefPtr(T* ptr, AdoptRefTag) : m_ptr(ptr) { }

        ......

        mutable T* m_ptr;
    };

    ......
};
           

       這個函數定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/PassRefPtr.h中。

       從這裡就可以看到,上述的PassRefPtr類的構造函數并沒有增加目标對象的引用計數,是以我們就可以在不改變目标對象的引用計數的前提下,将一個RefPtr智能指針傳遞給一個PassRefPtr智能指針,相當于就是将一個RefPtr智能指針對目标對象的所有權轉移給一個PassRefPtr智能指針。

       接下來我們再看WebKit的第二類智能指針的實作,它由類OwnPtr實作,如下所示:

namespace WTF {

    .......

    template<typename T> class OwnPtr {
        ......
    public:
        typedef typename RemoveExtent<T>::Type ValueType;
        typedef ValueType* PtrType;

        OwnPtr() : m_ptr(0) { }
        OwnPtr(std::nullptr_t) : m_ptr(0) { }

        ......
        OwnPtr(const PassOwnPtr<T>&);
        ......

#if !COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES)
        ......
        OwnPtr(const OwnPtr&);
#endif

        ~OwnPtr()
        {
            OwnedPtrDeleter<T>::deletePtr(m_ptr);
            m_ptr = 0;
        }

        ......

        PtrType get() const { return m_ptr; }

        ......

        PassOwnPtr<T> release();
        PtrType leakPtr() WARN_UNUSED_RETURN;

        ValueType& operator*() const { ASSERT(m_ptr); return *m_ptr; }
        PtrType operator->() const { ASSERT(m_ptr); return m_ptr; }

        ......

        OwnPtr& operator=(const PassOwnPtr<T>&);
        ......

#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES)
        OwnPtr(OwnPtr&&);
        ......

        OwnPtr& operator=(OwnPtr&&);
        ......
#endif

        ......

    private:
#if !COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES)
        // If rvalue references are supported, noncopyable takes care of this.
        OwnPtr& operator=(const OwnPtr&);
#endif
 
        ......

        PtrType m_ptr;
    };

    ......

    template<typename T> inline OwnPtr<T>::OwnPtr(const PassOwnPtr<T>& o)
        : m_ptr(o.leakPtr())
    {
    }

    ......

    template<typename T> inline PassOwnPtr<T> OwnPtr<T>::release()
    {
        PtrType ptr = m_ptr;
        m_ptr = 0;
        return PassOwnPtr<T>(ptr);
    }

    template<typename T> inline typename OwnPtr<T>::PtrType OwnPtr<T>::leakPtr()
    {
        PtrType ptr = m_ptr;
        m_ptr = 0;
        return ptr;
    }

    ......

    template<typename T> template<typename U> inline OwnPtr<T>& OwnPtr<T>::operator=(const PassOwnPtr<U>& o)
    {
        ......
        PtrType ptr = m_ptr;
        m_ptr = o.leakPtr();
        ASSERT(!ptr || m_ptr != ptr);
        OwnedPtrDeleter<T>::deletePtr(ptr);
        return *this;
    }

    ......

#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES)
    template<typename T> inline OwnPtr<T>::OwnPtr(OwnPtr<T>&& o)
        : m_ptr(o.leakPtr())
    {
    }

    ......

    template<typename T> inline OwnPtr<T>& OwnPtr<T>::operator=(OwnPtr<T>&& o)
    {
        PtrType ptr = m_ptr;
        m_ptr = o.leakPtr();
        ASSERT(!ptr || m_ptr != ptr);
        OwnedPtrDeleter<T>::deletePtr(ptr);

        return *this;
    }

    ......
#endif

    ......
} // namespace WTF
           

       這個類定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/OwnPtr.h中。

       從OwnPtr類的定義可以看到,OwnPtr智能指針隻能通過以下構造函數建立:

template<typename T> inline OwnPtr<T>::OwnPtr(const PassOwnPtr<T>& o)
        : m_ptr(o.leakPtr())
    {
    }
           

       也就是說,我們建立了一個T對象後,隻能先用來建立一個PassOwnPtr對象,然後再用該PassOwnPtr對象來建立一個OwnPtr智能指針。

       當然,我們也可以使用OwnPtr類的以下構造函數來建立一個沒有引用任何目标對象的OwnPtr智能指針:

OwnPtr() : m_ptr(0) { }
        OwnPtr(std::nullptr_t) : m_ptr(0) { }
           

       然後,再通過調用操作符重載函數operator=将一個PassOwnPtr對象指派給OwnPtr智能指針:

template<typename T> template<typename U> inline OwnPtr<T>& OwnPtr<T>::operator=(const PassOwnPtr<U>& o)
    {
        ......
        PtrType ptr = m_ptr;
        m_ptr = o.leakPtr();
        ASSERT(!ptr || m_ptr != ptr);
        OwnedPtrDeleter<T>::deletePtr(ptr);
        return *this;
    }
           

       無論是哪一種方式建立OwnPtr智能指針,都必須先建立一個PassOwnPtr對象。PassOwnPtr類描述的也是一個智能指針,它與OwnPtr的關系有點類似我前面分析的RefPtr與PassOwnPtr的關系。也就是說,PassOwnPtr智能指針可以作為函數參數或者函數傳回值傳遞。後面我們再詳細分析PassOwnPtr智能指針的建立過程。

       PassOwnPtr類有一個成員函數leakPtr,它的作用類似于前面分析的PassRefPtr類的成員函數leakPtr,都是用來放棄一個PassOwnPtr智能指針對目标對象的引用的。在上面描述的兩種OwnPtr智能指針建立方式中,被PassOwnPtr智能指針放棄引用的目标對象将被正在建立的OwnPtr智能指針接管。也就是說,對目标對象的引用從PassOwnPtr智能指針轉移到了OwnPtr智能指針。

       不過對于通過操作符重載函數operator=建立的OwnPtr智能指針,如果它之前有引用其它的目标對象,那麼就要求該對象與參數o描述的PassOwnPtr智能指針引用的目标對象是不一樣的,并且該對象會被釋放掉。也就是說,當一個OwnPtr智能指針要引用新的目标對象時,必須先釋放之前引用的舊目标對象。

       一個OwnPtr智能指針在超出其生命周期範圍之前,如果想要放棄對目标對象的引用,那麼可以通過調用OwnPtr類的成員函數release或者leakPtr來實作,如下所示:

template<typename T> inline PassOwnPtr<T> OwnPtr<T>::release()
    {
        PtrType ptr = m_ptr;
        m_ptr = 0;
        return PassOwnPtr<T>(ptr);
    }

    template<typename T> inline typename OwnPtr<T>::PtrType OwnPtr<T>::leakPtr()
    {
        PtrType ptr = m_ptr;
        m_ptr = 0;
        return ptr;
    }
           

       OwnPtr類的成員函數release将OwnPtr智能指針放棄引用的目标對象轉移給了一個PassOwnPtr智能指針,這意味着可以将一個OwnPtr智能指針轉化為一個PassOwnPtr指針,進而使得它引用的目标對象可以作為函數參數或者函數傳回值傳遞。

       OwnPtr類的成員函數leakPtr是直接放棄OwnPtr智能指針引用的目标對象,不過它會傳回該目标對象的位址值給調用者,也就是将一個指向目标對象的裸指針傳回給調用者。

       我們注意到,OwnPtr類的成員函數leakPtr的傳回值類型為OwnPtr<T>::PtrType,它的定義如下所示:

typedef typename RemoveExtent<T>::Type ValueType;
        typedef ValueType* PtrType;
           

       RemoveExtent是一個與C++ 11引入的右值引用(Rvalue Reference)有關一個模闆類。右值引用通過T&&符号來表示,它主要是用來實作move語意和完美轉發(perfect forwarding)。關于右值引用的概念,由于篇幅關系,這裡不進行展開,然後強烈建議大家去看看這兩篇文章: VC10中的C++0x特性和 Rvalue Reference Declarator: &&。這裡我們隻需要知道,ValueType描述的是模闆參數T的值類型,而PtrType描述的是模闆參數T的裸指針類型,不管我們是使用T、T&還是T&&來推導模闆參數。

       關于右值引用的move語意,這裡值得再解釋一下。簡單地說,move語意是為了消除臨時對象之間的拷貝操作,例如,對于以下表達式來說:

string s = string("h") + "e" + "ll" + "o";
           

       string("h")會産生一個臨時對象。這個臨時對象與"e"相加後,又會産生另外一個臨時對象。依次類推,直到"o"相加完成之後。而且最後生成的臨時對象又會被拷貝給字元串s。這些中間的臨時對象都是沒有必要生成的,而且字元串s也不需要去拷貝最後生成的臨時對象得到。我們完全可以隻生成一個臨時對象,也就是由string("h")産生一個臨時對象,此後直接将後面的"e"、"ll"和"o"追加在此臨時對象内部的字元緩沖區上,并且最後将該臨時對象内部所擁有的字元緩沖區轉移給字元串s。這樣就可以在一定程度上提高字元串的連接配接效率,而這就是所謂的move語意,它是與copy意義相對的。

       一個類要實作move語意,必須要提供move構造函數和move指派操作符函數,就好像一個類要實作copy語意,要提供copy構造函數copy指派操作符函數一樣,隻不過編譯器不會為一個類提供預設的move構造函數和move指派操作符函數。

       我們看到,在編譯器支援右值引用的情況下,OwnPtr類實作了move構造函數和move指派操作符函數,如下所示:

#if COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES)
    template<typename T> inline OwnPtr<T>::OwnPtr(OwnPtr<T>&& o)
        : m_ptr(o.leakPtr())
    {
    }

    ......

    template<typename T> inline OwnPtr<T>& OwnPtr<T>::operator=(OwnPtr<T>&& o)
    {
        PtrType ptr = m_ptr;
        m_ptr = o.leakPtr();
        ASSERT(!ptr || m_ptr != ptr);
        OwnedPtrDeleter<T>::deletePtr(ptr);

        return *this;
    }

    ......
#endif
           

       一個類實作的move構造函數和move指派操作符函數的參數必須是一個右值引用,并且它們的作用是将一個對象擁有的資源轉移給另外一個對象。以OwnPtr類為例,一個OwnPtr智能指針擁有的資源就是對目标對象的引用,也就是它的的move構造函數和move指派操作符函數的作用是将一個OwnPtr智能指針引用的目标對象轉移給另外一個OwnPtr智能指針引用。這樣就既不違反我們前面說的一個目标對象在同一時刻隻能被一個第二類智能指針引用的原則,同時又能夠實作OwnPtr類的move語意。

       同時,我們也可以看到,在編譯器不支援右值引用的情況下,OwnPtr類隻是聲明了copy構造函數copy指派操作符函數,但是沒有實作它們。這就意味着,我們不能将一個OwnPtr智能指針拷貝給另外一個,因為這會違反我們前面說的一個目标對象在同一時刻隻能被一個第二類智能指針引用的原則。

       與前面分析的RefPtr和PassRefPtr智能指針類似,OwnPtr智能指針也提供了兩個成員操作符重載函數operator*和operator->來直接操作目标對象,例如調用目标對象的成員函數,如下所示:

ValueType& operator*() const { ASSERT(m_ptr); return *m_ptr; }
        PtrType operator->() const { ASSERT(m_ptr); return m_ptr; }
           

       當一個OwnPtr智能指針超出其生命周期範圍内時,它所引用的對象就會被釋放掉,如下所示:

~OwnPtr()
        {
            OwnedPtrDeleter<T>::deletePtr(m_ptr);
            m_ptr = 0;
        }
           

       進而就可以起到自動釋放不再需要的對象的作用。

       從這裡可以看到,OwnPtr智能指針是通過模闆類OwnedPtrDeleter<T>的成員函數deletePtr來釋放目标對象的,它的實作如下所示:

template <typename T>
struct OwnedPtrDeleter {
    static void deletePtr(T* ptr)
    {
        COMPILE_ASSERT(!IsRefCounted<T>::value, UseRefPtrForRefCountedObjects);
        COMPILE_ASSERT(sizeof(T) > 0, TypeMustBeComplete);
        delete ptr;
    }
};
           

       這個函數定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/OwnPtrCommon.h中。

       模闆類OwnedPtrDeleter<T>的成員函數deletePtr在編譯時會通過宏COMPILE_ASSERT來斷言參數ptr描述的對象不是從前面我們分析的RefCountedBase類或者ThreadSafeRefCountedBase繼承下來的。這意味着OwnPtr智能指針不能用來引用具有引用計數功能的對象。這實際上是定義了這樣的一個規則:具有引用計數功能的對象,如果要配合智能指針使用,那麼就使用RefPtr或者PassRefPtr智能指針;否則的話,就使用OwnPtr或者我們接下來分析的PassOwnPtr智能指針。這個規則要求我們對程式裡面實作的類有一個清晰的設計。

       宏COMPILE_ASSERT實際上是利用C++ 11引入的static_assert特性來實作的。這個static_assert語句不會對編譯後得到的二進制代碼有任何影響,純粹是編譯期間使用的,用來在編譯期間就能捕捉到錯誤。

       從模闆類OwnedPtrDeleter<T>的成員函數deletePtr的實作可以知道,當模闆類IsRefCounted<T>的靜态成員變量value的值等于true的時候,就說明參數ptr描述的對象是從RefCountedBase類或者ThreadSafeRefCountedBase繼承下來的。

      模闆類IsRefCounted<T>的靜态成員變量value的定義如下所示:

template<typename T>
struct IsRefCounted {
    static const bool value = IsSubclass<T, RefCountedBase>::value
        || IsSubclass<T, ThreadSafeRefCountedBase>::value;
};
           

      這個成員變量定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/OwnPtrCommon.h中。

      從這裡就可以看到,如果模闆參數T是從RefCountedBase類或者ThreadSafeRefCountedBase類繼承下來的,那麼模闆類IsRefCounted<T>的靜态成員變量value就會等于true。

      判斷模闆參數T是不是從RefCountedBase類或者ThreadSafeRefCountedBase類繼承下來,是通過調用模闆類IsSubclass<T>的靜态成員變量value來實作的。模闆類IsSubclass<T>的靜态成員變量value的定義如下所示:

namespace WTF {
    ......

    template <typename T, typename U> class IsSubclass {
        typedef char YesType;
        struct NoType {
            char padding[8];
        };

        static YesType subclassCheck(U*);
        static NoType subclassCheck(...);
        static T* t;
    public:
        static const bool value = sizeof(subclassCheck(t)) == sizeof(YesType);
    };

    ......
}
           

       這個成員變量定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/TypeTraits.h中。

       這裡的模闆參數U就為RefCountedBase或者ThreadSafeRefCountedBase,判斷模闆參數T是不是它們的子類用了一個很巧妙方法。

       模闆類IsSubclass<T>定義了兩個版本的靜态成員函數subclassCheck,其中一個的參數為U*,另一個的參數為可變參數。如果T是從U繼承下來的,T*就可以自動轉化為U*,這意味着調用靜态成員函數subclassCheck(t),也就subclassCheck(T*),會自動比對為參數為U*的成員函數subclassCheck。參數為U*的成員函數的傳回值為YesType,這就意味着參數T是參數U的子類。

       注意,sizeof是一個編譯器運算符号,當它的參數是一個函數的時候,實際上計算的是該函數的傳回值的類型所占據的位元組數。由于函數的傳回值類型通過聲明就可以知道,是以模闆類IsSubclass<T>就隻是聲明了靜态成員函數subclassCheck,而沒有對應的實作。

       前面提到,為了建立OwnPtr智能指針,我們首先要建立PassOwnPtr智能指針。PassOwnPtr智能指針使得OwnPtr智能指針引用的對象也可以作為函數參數或者函數傳回值傳遞,它的定義如下所示:

namespace WTF {

    ......

    template<typename T> class PassOwnPtr {
        ......
    public:
        typedef typename RemoveExtent<T>::Type ValueType;
        typedef ValueType* PtrType;

        PassOwnPtr() : m_ptr(0) { }
        PassOwnPtr(std::nullptr_t) : m_ptr(0) { }      

        ......
  
        PassOwnPtr(const PassOwnPtr& o) : m_ptr(o.leakPtr()) { }
        ......

        ~PassOwnPtr() { OwnedPtrDeleter<T>::deletePtr(m_ptr); }

        PtrType get() const { return m_ptr; }

        PtrType leakPtr() const WARN_UNUSED_RETURN;

        ValueType& operator*() const { ASSERT(m_ptr); return *m_ptr; }
        PtrType operator->() const { ASSERT(m_ptr); return m_ptr; }
 
        ......

        template<typename U> friend PassOwnPtr<U> adoptPtr(U*);
 
        ......

    private:
        explicit PassOwnPtr(PtrType ptr) : m_ptr(ptr) { }

        ......

        mutable PtrType m_ptr;
    };

    ......

    template<typename T> inline typename PassOwnPtr<T>::PtrType PassOwnPtr<T>::leakPtr() const
    {
        PtrType ptr = m_ptr;
        m_ptr = 0;
        return ptr;
    }

    ......

    template<typename T> inline PassOwnPtr<T> adoptPtr(T* ptr)
    {
        return PassOwnPtr<T>(ptr);
    }

    ......
};
           

       這個類定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/PassOwnPtr.h。

       PassOwnPtr類的實作與OwnPtr類的實作是類似的,最主要的差別在于兩點:

       1. PassOwnPtr類提供了一個拷貝構造函數,通過此拷貝構造函數可以用一個PassOwnPtr智能指針構造另外一個PassOwnPtr智能指針,不過這樣會導緻原來的PassOwnPtr智能指針失去對目标對象的引用,因為目标對象轉為被新構造的PassOwnPtr智能指針引用了。這意味着PassOwnPtr智能指針可以用來傳遞它所引用的目标對象。

       2. PassOwnPtr類有一個友員函數adoptPtr,通過這個友員函數可以調用PassOwnPtr類的一個私有構造函數為一個目标對象建立一個PassOwnPtr智能指針。這意味PassOwnPtr智能指針可以直接引用一個目标對象,而不像OwnPtr智能指針要通過另外一個PassOwnPtr智能指針來引用一個目标對象。

       以上就是WebKit實作的第一類智能指針RefPtr/PassRefPtr和第二類智能指針OwnPtr/PassOwnPtr,接下來我們繼續分析WebKit實作的弱智能指針。

       WebKit的弱智能指針由WeakPtr類實作,如下所示:

template<typename T>
class WeakPtr {
    ......
public:
    ......
    WeakPtr(PassRefPtr<WeakReference<T> > ref) : m_ref(ref) { }

    T* get() const { return m_ref ? m_ref->get() : 0; }
    void clear() { m_ref.clear(); }

    ......

private:
    RefPtr<WeakReference<T> > m_ref;
};
           

       這個類定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/WeakPtr.h中。 

       從WeakPtr類的構造函數可以知道,建立一個WeakPtr弱智能指針需要一個WeakReference對象。這個WeakReference對象就儲存在WeakPtr類的成員變量m_ref中。

       在使用一個WeakPtr弱智能指針,首先要調用它的成員函數get檢查它引用的目标對象是否還存在。如果存在,WeakPtr類的成員函數get就會傳回一個指向目标對象的指針給調用者。否則的話,WeakPtr類的成員函數get傳回一個空指針。

       WeakPtr類的成員函數get又是通過成員變量m_ref描述的一個WeakReference對象的成員函數get檢查一個WeakPtr智能指針引用的目标對象是否還存在的。

       WeakReference類的實作如下所示:

template<typename T>
class WeakReference : public ThreadSafeRefCounted<WeakReference<T> > {
    ......
public:
    static PassRefPtr<WeakReference<T> > create(T* ptr) { return adoptRef(new WeakReference(ptr)); }
    static PassRefPtr<WeakReference<T> > createUnbound() { return adoptRef(new WeakReference()); }

    T* get() const
    {
        ......
        return m_ptr;
    }

    void clear()
    {
        ......
        m_ptr = 0;
    }

    void bindTo(T* ptr)
    {
        ......
        m_ptr = ptr;
    }

private:
    WeakReference() : m_ptr(0) { }

    explicit WeakReference(T* ptr)
        : m_ptr(ptr)
          ......
    {
    }

    T* m_ptr;
    ......
};
           

       這個類定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/WeakPtr.h中。 

       WeakReference類的成員變量m_ptr儲存的就是一個WeakPtr弱智能指針所引用的目标對象的位址值。由于WeakPtr弱智能指針所引用的目标對象有可能是已經被銷毀了的,是以WeakReference類的成員變量m_ptr儲存的可能是一個無效的位址值。

       我們可以通過兩種方式建立一個WeakReference對象。第一種方式是提供一個目标對象T,然後調用WeakReference類的靜态成員函數create來建立。第二種方式是先調用WeakReference類的靜态成員函數createUnbound建立一個成員變量m_ptr被初始化0的WeakReference對象,然後再調用該WeakReference對象的成員函數bindTo将成員變量m_ptr指向一個目标對象。

       前面提到,一個WeakPtr弱智能指針引用的目标對象有可能是已經被銷毀了的。當這種情況發生時,我們需要調用與該WeakPtr弱智能指針與關聯的WeakReference對象的成員函數clear,将其成員變量m_ptr的值設定為0。這樣以後我們調用該WeakReference對象的成員函數get時,就會得到一個0值。也就是說,當一個WeakPtr弱智能指針引用的目标對象有可能被銷毀之後,我們調用它的成員函數get獲得的傳回值是等于0的。

       那麼我們是怎麼知道一個WeakPtr弱智能指針引用的目标對象正在被銷毀的呢?要回答這個問題,我們首先觀察WeakPtr弱智能指針是如何使用的。一般來說,如果某一個類的對象可以配合WeakPtr弱智能指針來引用,那麼該類在定義時,就需要引入一個類型為WeakPtrFactory的成員變量,如以下的HTMLDocumentParser類所示:

class HTMLDocumentParser :  public ScriptableDocumentParser, private HTMLScriptRunnerHost {
    ......

protected:
    ......

    HTMLDocumentParser(HTMLDocument&, bool reportErrors);

    ......

private:
    ......

    WeakPtrFactory<HTMLDocumentParser> m_weakFactory;

    ......
};
           

       這個類定義在檔案external/chromium_org/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h中。

       在建立一個HTMLDocumentParser對象的時候,它的成員變量m_weakFactory就會被初始化,如下所示:

HTMLDocumentParser::HTMLDocumentParser(HTMLDocument& document, bool reportErrors)
    : ......
    , m_weakFactory(this)
      ......
{
    ......
}
           

       這個函數定義在檔案external/chromium_org/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp中。

       WeakPtrFactory類的定義如下所示:

template<typename T>
class WeakPtrFactory {
    ......
public:
    explicit WeakPtrFactory(T* ptr) : m_ref(WeakReference<T>::create(ptr)) { }

    WeakPtrFactory(PassRefPtr<WeakReference<T> > ref, T* ptr)
        : m_ref(ref)
    {
        m_ref->bindTo(ptr);
    }

    ~WeakPtrFactory() { m_ref->clear(); }

    // We should consider having createWeakPtr populate m_ref the first time createWeakPtr is called.
    WeakPtr<T> createWeakPtr() { return WeakPtr<T>(m_ref); }

    ......

private:
    RefPtr<WeakReference<T> > m_ref;
};
           

       這個類定義在檔案external/chromium_org/third_party/WebKit/Source/wtf/WeakPtr.h中。 

       回到前面HTMLDocumentParser類的構造函數中,當一個HTMLDocumentParser對象建立時,它的位址值就會作為參數傳遞給WeakPtrFactory類的以T*為參數的構造函數,以便可以建立一個WeakPtrFactory對象。這個構造函數再以該HTMLDocumentParser對象的位址值為參數,調用WeakReference類的靜态成員函數create建立一個WeakReference對象,儲存在WeakPtrFactory類的成員變量m_ref中。

       當然我們也可以調用WeakPtrFactory類的另外一個構造函數建立一個WeakPtrFactory對象,不過我們需要提供一個WeakReference對象,以及一個目标對象的位址。在這種情況下,WeakPtrFactory類的構造函數除了會将調用者提供的WeakReference對象儲存在成員變量m_ref之外,還會将目标對象的位址綁定到調用者提供的WeakReference對象中。

       建立好一個WeakPtrFactory對象之後,就可以調用它的成員函數createWeakPtr擷取一個WeakPtr弱智能指針了,擷取到的WeakPtr弱智能指針是根據WeakPtrFactory對象的成員變量m_ref描述的WeakReference對象建立的。

       當一個WeakPtr弱智能指針引用的目标對象被銷毀時,例如當一個HTMLDocumentParser對象被銷毀時,它的成員變量m_weakFactory描述的WeakPtrFactory對象也會随之銷毀,而當一個WeakPtrFactory對象被銷毀時,它的析構函數就會被調用,随後這個WeakPtrFactory對象的成員變量m_ref描述的一個WeakReference對象的成員函數clear也會被調用,最後就會導緻該WeakReference對象的成員變量m_ptr的值設定為0。這就意味着當一個WeakPtr弱智能指針引用的目标對象被銷毀時,與它關聯的一個WeakReference對象的成員變量m_ptr的值就會被設定為0,是以這時候調用該WeakPtr弱智能指針的成員函數get,就會獲得一個0值。

       以上就是WebKit的弱智能指針的實作原理,它的核心就是通過組合方式給目标對象關聯一個類型為WeakPtrFactory的成員變量,使得目标對象銷毀時,該成員變量指向的WeakPtrFactory對象被析構。一個WeakPtrFactory對象在析構的過程中,又會将其内部的一個WeakReference對象的成員變量m_ptr的值設定為0。由于該WeakReference對象又是與WeakPtr弱智能指針關聯的,是以WeakPtr弱智能指針通過該WeakReference對象的成員變量m_ptr就可以知道它所引用的目标是否已經被銷毀。

       接下來我們繼續分析Chromium實作的第一類智能指針和第二類智能指針,以及弱智能指針。

       Chromium的第一類智能指針由類scoped_refptr實作。由于它要求被引用對象具有計數功能,是以就提供了一個具有計數功能的基類RefCounted。當一個對象可以被類scoped_refptr描述的對象引用時,它就必須要從基類RefCounted繼承下來。這一點與WebKit的第一類智能指針RefPtr是類似的。

       Chromium的RefCounted類的實作如下所示:

template <class T>
class RefCounted : public subtle::RefCountedBase {
 public:
  RefCounted() {}

  void AddRef() const {
    subtle::RefCountedBase::AddRef();
  }

  void Release() const {
    if (subtle::RefCountedBase::Release()) {
      delete static_cast<const T*>(this);
    }
  }

 ......
};
           

       這個類定義在檔案external/chromium_org/base/memory/ref_counted.h中。

       Chromium的RefCounted類定義了兩個成員函數AddRef和Release,分别用來增加和減少目标對象的1個引用計數,并且都是通過調用父類RefCountedBase的成員函數AddRef和Release來實作的。當減少目标對象的1引用計數之後,如果目标對象的引用計數變為0,那麼目标對象就會被delete掉。

       Chromium的RefCountedBase類的定義如下所示:

class BASE_EXPORT RefCountedBase {
 ......
 protected:
  RefCountedBase()
      : ref_count_(0)
  ......
      {
  }

  ......

  void AddRef() const {
    ......
    ++ref_count_;
  }

  bool Release() const {
    ......
    if (--ref_count_ == 0) {
      ......
      return true;
    }
    return false;
  }

 private:
  mutable int ref_count_;

  ......
};
           

       這個類定義在檔案external/chromium_org/base/memory/ref_counted.h中。

       Chromium的RefCountedBase類與WebKit的RefCountedBase類也是幾乎一樣,都是提供了非線程安全版本的計數功能。

       Chromium提供的線程安全版本的具有計數功能的基類是RefCountedThreadSafe,它的實作如下所示:

template <class T, typename Traits = DefaultRefCountedThreadSafeTraits<T> >
class RefCountedThreadSafe : public subtle::RefCountedThreadSafeBase {
 public:
  ......

  void AddRef() const {
    subtle::RefCountedThreadSafeBase::AddRef();
  }

  void Release() const {
    if (subtle::RefCountedThreadSafeBase::Release()) {
      Traits::Destruct(static_cast<const T*>(this));
    }
  }

  ......

 private:
  friend struct DefaultRefCountedThreadSafeTraits<T>;
  static void DeleteInternal(const T* x) { delete x; }
  ......
};
           

       這個類定義在檔案external/chromium_org/base/memory/ref_counted.h中。

       Chromium的RefCountedThreadSafe類與RefCountedBase類的實作也類似,不過有兩點差別:

       1. RefCountedThreadSafe類是從RefCountedThreadSafeBase類繼承下來的,後者提供了線程安全版本的計數功能。

       2. RefCountedThreadSafe類可以通過模闆參數Traits指定一個類,當它引用的目标對象的引用計數等于0的時候,就會調用該參數Traits指定的類的靜态成員函數Destruct來釋放目标對象。

       第2點是很有用的。比如我們在一個線程建立一個T對象,這個T對象可能會被其它線程引用。當這個T對象的引用計數是在其它線程減少為0時,我們希望它不要在其它線程釋放,而是要在建立線程進行釋放,那麼就可以通過指定參數Traits來實作。

       如果沒有指定模闆參數Traits,那麼它的預設值就為DefaultRefCountedThreadSafeTraits。這個DefaultRefCountedThreadSafeTraits類的實作如下所示:

template<typename T>
struct DefaultRefCountedThreadSafeTraits {
  static void Destruct(const T* x) {
    ......
    RefCountedThreadSafe<T,
                         DefaultRefCountedThreadSafeTraits>::DeleteInternal(x);
  }
};
           

       這個類定義在檔案external/chromium_org/base/memory/ref_counted.h中。

       DefaultRefCountedThreadSafeTraits類的靜态成員函數Destruct又是通過調用RefCountedThreadSafe類的靜态成函數DeleteInternal來釋放目标對象的。從RefCountedThreadSafe類的實作可以知道,它的靜态成員函數DeleteInternal是直接将目标對象delete掉。

       我們再來看RefCountedThreadSafe類的父類RefCountedThreadSafeBase的實作,如下所示:

class BASE_EXPORT RefCountedThreadSafeBase {
 ......

 protected:
  ......

  void AddRef() const;

  // Returns true if the object should self-delete.
  bool Release() const;

 private:
  mutable AtomicRefCount ref_count_;
  ......
};
           

       這個類定義在檔案external/chromium_org/base/memory/ref_counted.h中。

       RefCountedThreadSafeBase類提供了兩個成員函數AddRef和Release,用來執行線程安全的引用計數加1和減1操作,它們的實作如下所示:

void RefCountedThreadSafeBase::AddRef() const {
  ......
  AtomicRefCountInc(&ref_count_);
}

bool RefCountedThreadSafeBase::Release() const {
  ......
  if (!AtomicRefCountDec(&ref_count_)) {
    ......
    return true;
  }
  return false;
}
           

       這兩個函數定義在檔案external/chromium_org/base/memory/ref_counted.cc中。

       RefCountedThreadSafeBase類的成員函數AddRef和Release分别通過函數AtomicRefCountInc和AtomicRefCountDec對目标對象的引用計數執行原子性的加1和減1操作,是以它們是線程安全的。

      有了RefCounted和ThreadSafeRefCounted這兩個基類為對象提供計數功能之後,我們就繼續分析Chromium的第一類智能指針scoped_refptr的實作,如下所示:

template <class T>
class scoped_refptr {
 public:
  typedef T element_type;

  scoped_refptr() : ptr_(NULL) {
  }

  scoped_refptr(T* p) : ptr_(p) {
    if (ptr_)
      ptr_->AddRef();
  }

  scoped_refptr(const scoped_refptr<T>& r) : ptr_(r.ptr_) {
    if (ptr_)
      ptr_->AddRef();
  }

  template <typename U>
  scoped_refptr(const scoped_refptr<U>& r) : ptr_(r.get()) {
    if (ptr_)
      ptr_->AddRef();
  }

  ~scoped_refptr() {
    if (ptr_)
      ptr_->Release();
  }

  T* get() const { return ptr_; }

  // Allow scoped_refptr<C> to be used in boolean expression
  // and comparison operations.
  operator T*() const { return ptr_; }

  T* operator->() const {
    assert(ptr_ != NULL);
    return ptr_;
  }

  scoped_refptr<T>& operator=(T* p) {
    // AddRef first so that self assignment should work
    if (p)
      p->AddRef();
    T* old_ptr = ptr_;
    ptr_ = p;
    if (old_ptr)
      old_ptr->Release();
    return *this;
  }

  ......

 protected:
  T* ptr_;
};
           

       這個類定義在檔案external/chromium_org/base/memory/ref_counted.h中。

       Chromium的scoped_refptr類的實作與WebKit的RefPtr類的實作幾乎是一樣的,并且都是要求目标對象是從RefCounted類或者ThreadSafeRefCounted類繼承下來的,是以我們就不再詳述。

       為了友善建立scoped_refptr智能指針,Chromium提供了一個幫助函數make_scoped_refptr,它的實作如下所示:

template <typename T>
scoped_refptr<T> make_scoped_refptr(T* t) {
  return scoped_refptr<T>(t);
}
           

      這個函數定義在檔案external/chromium_org/base/memory/ref_counted.h中。

      函數make_scoped_refptr調用scoped_refptr類的構造函數直接将參數t封裝成一個scoped_refptr智能指針,然後傳回給調用者。       

      接下來我們再看Chromium的第二類智能指針的實作,它由類scoped_ptr實作,如下所示:

template <class T, class D = base::DefaultDeleter<T> >
class scoped_ptr {
  MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)

  COMPILE_ASSERT(base::internal::IsNotRefCounted<T>::value,
                 T_is_refcounted_type_and_needs_scoped_refptr);

 public:
  // The element and deleter types.
  typedef T element_type;
  typedef D deleter_type;

  // Constructor.  Defaults to initializing with NULL.
  scoped_ptr() : impl_(NULL) { }

  // Constructor.  Takes ownership of p.
  explicit scoped_ptr(element_type* p) : impl_(p) { }

  // Constructor.  Allows initialization of a stateful deleter.
  scoped_ptr(element_type* p, const D& d) : impl_(p, d) { }

  ......

  // Constructor.  Move constructor for C++03 move emulation of this type.
  scoped_ptr(RValue rvalue) : impl_(&rvalue.object->impl_) { }

  ......

  // Accessors to get the owned object.
  // operator* and operator-> will assert() if there is no current object.
  element_type& operator*() const {
    ......
    return *impl_.get();
  }
  element_type* operator->() const  {
    ......
    return impl_.get();
  }
  element_type* get() const { return impl_.get(); }

  ......

  template <typename PassAsType>
  scoped_ptr<PassAsType> PassAs() {
    return scoped_ptr<PassAsType>(Pass());
  }

 private:
  ......
  base::internal::scoped_ptr_impl<element_type, deleter_type> impl_;

  ......
};
           

       這個類定義在檔案external/chromium_org/base/memory/scoped_ptr.h中。

       Chromium的scoped_ptr類與WebKit的OwnPtr類的實作也是類似的:

       1. 通過宏COMPILE_ASSERT以及IsNotRefCounted<T>類的靜态成員變量value禁止scoped_ptr智能指針引用的目标對象從RefCounted類或者ThreadSafeRefCounted類繼承下來,也就是不要求目标對象具有引用計數功能。

       2. 通過宏MOVE_ONLY_TYPE_FOR_CPP_03禁止scoped_ptr智能指針的copy語意,但是提供move語意。

       不同的地方在于:

       1. 可以直接根據目标對象建立scoped_ptr智能指針。WebKit要先根據目标對象建立PassOwnPtr智能指針,再根據PassOwnPtr智能指針建立OwnPtr智能指針。

       2. Chromium不像WebKit一樣,單獨提供了一個可以用來傳遞目标對象的PassOwnPtr智能指針,而是直接在scoped_ptr智能指針中內建了該功能,也就是通過scoped_ptr類的成員函數PassAs來實作。

       3. scoped_ptr類将引用的目标對象間接儲存在成員變量impl_描述的一個scoped_ptr_impl對象中。

       類scoped_ptr_impl的實作如下所示:

class scoped_ptr_impl {
 public:
  explicit scoped_ptr_impl(T* p) : data_(p) { }

  // Initializer for deleters that have data parameters.
  scoped_ptr_impl(T* p, const D& d) : data_(p, d) {}
 
  template <typename U typename V>
  scoped_ptr_impl(scoped_ptr_impl<U, V>* other)
      : data_(other->release(), other->get_deleter()) {
    ......
  }

  ......

  ~scoped_ptr_impl() {
    if (data_.ptr != NULL) {
      ......
      static_cast<D&>(data_)(data_.ptr);
    }
  }

  ......

  T* get() const { return data_.ptr; }

  ......

  T* release() {
    T* old_ptr = data_.ptr;
    data_.ptr = NULL;
    return old_ptr;
  }

  ......

 private:
  ......

  struct Data : public D {
    explicit Data(T* ptr_in) : ptr(ptr_in) {}
    Data(T* ptr_in, const D& other) : D(other), ptr(ptr_in) {}
    T* ptr;
  };

  Data data_;

  ......
};
           

       這個類定義在檔案external/chromium_org/base/memory/scoped_ptr.h中。

       類scoped_ptr_impl将目标對象儲存在成員變量data_描述的一個Data對象的成員變量ptr中。同時,我們可以通過模闆參數D指定一個類,當scoped_ptr_impl智能指針超出其生命周期範圍時,目标對象通過調用該類的操作符重載函數operator()來釋放。這一點與scoped_refptr智能指針的實作是類似的。

       此外,類scoped_ptr_impl還提供了一個release成員函數和一個拷貝構造函數,前者用來釋放對目标對象的引用,後者卻不是直接将參數描述的scoped_ptr_impl對象拷貝到目前正在構造的scoped_ptr_impl對象,而是将參數描述的scoped_ptr_impl對象引用的目标對象轉移給目前構造的scoped_ptr_impl對象,是以這個構造函數實作的實際上是一個move語意的構造函數,而不是一個copy語意的構造函數。

       接下來,我們繼續分析scoped_ptr智能指針是如何通過宏MOVE_ONLY_TYPE_FOR_CPP_03來禁止copy語意和實作move語意義的。

       宏MOVE_ONLY_TYPE_FOR_CPP_03的定義如下所示:

#define MOVE_ONLY_TYPE_FOR_CPP_03(type, rvalue_type) \
 private: \
  struct rvalue_type { \
    explicit rvalue_type(type* object) : object(object) {} \
    type* object; \
  }; \
  type(type&); \
  void operator=(type&); \
 public: \
  operator rvalue_type() { return rvalue_type(this); } \
  type Pass() { return type(rvalue_type(this)); } \
  typedef void MoveOnlyTypeForCPP03; \
 private:
           

       這個宏定義在檔案external/chromium_org/base/move.h中。

       是以,scoped_ptr類内部的宏MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)展開後就得到:

private:
  struct RValue { 
    explicit RValue(scoped_ptr* object) : object(object) {}
    scoped_ptr* object; 
  }; 
  scoped_ptr(scoped_ptr&);
  void operator=(scoped_ptr&);
 public:
  operator RValue() { return RValue(this); }
  scoped_ptr Pass() { return scoped_ptr(RValue(this)); }
  typedef void MoveOnlyTypeForCPP03;
 private:
           

       這意味着在scoped_ptr類的内部,有一個結構體RValue。這個RValue結構體就是用來實作C++ 11的右值引用的,但是這裡不是通過T&&關鍵字來描述右值引用的。這意味着即使是在不支援C++ 11的編譯器中,scoped_ptr也能實作move語意。

       同時,scoped_ptr類的拷貝構造函數和指派操作符函數被聲明為私有的,并且沒有相應的實作。這意味着scoped_ptr智能指針不能拷貝給另外一個scoped_ptr智能指針,因為這樣會違反一個目标對象在同一時刻被多個第二類智能指針引用的原則。

       此外,scoped_ptr類提供了兩個成員函數RValue和Pass。其中,前者用來将一個scoped_ptr智能指針封裝成一個RValue結構體,也就是一個右值引用,後者将一個scoped_ptr智能指針引用的目标對象轉移給另外一個scoped_ptr智能指針。scoped_ptr類的這兩個成員函數以及以const RValue&為參數的構造函數一起實作了scoped_ptr智能指針的move語意。

       我們通過以下代碼片斷說明scoped_ptr智能指針的move語意的實作過程,如下所示:

scoped_ptr<T> p1(new T());
scoped_ptr<T> p2 = p1.Pass();
           

       首先,scoped_ptr智能指針p1引用了一個目标對象,接着,我們要将scoped_ptr智能指針p1引用的目标對象轉移給scoped_ptr智能指針p2。

       調用scoped_ptr智能指針p1的成員函數Pass的時候,發生了以下事情:

       1. 建立了一個RValue臨時對象。

       2. 建立了一個scoped_ptr臨時對象。

       第2步建立的scoped_ptr臨時對象接下來被指派給scoped_ptr智能指針p2,這時候又會發生以下事情:

       1. 由于scoped_ptr類的拷貝構造函數被聲明為私有的,并且沒相應的實作,是以,上述建立的scoped_ptr臨時對象不能通過scoped_ptr類的拷貝構造函數來構造scoped_ptr智能指針p2,也就是不能通過scoped_ptr類的拷貝構造函數将scoped_ptr智能指針p1引用的目标對象轉移給scoped_ptr智能指針p2。

       2. scoped_ptr類實作了一個以const RValue&為參數的構造函數,是以前面建立的scoped_ptr臨時對象首先會被scoped_ptr類的操作符成員函數operator RValue轉化為一個臨時的RValue對象。

       在scoped_ptr類以const RValue&為參數的構造函數的調用過程中又會發生以下事情:

       1. 參數rvalue描述的RValue對象的成員變量object描述的scoped_ptr智能指針(也就是scoped_ptr智能指針p1)的成員變量impl_将會用來初始化正在構造的智能指針(也就是scoped_ptr智能指針p2)的成員變量impl_,也就是會調用前面分析的scoped_ptr_impl類的拷貝構造函數。

       2. 由于scoped_ptr_impl類的拷貝構造函數實作的是move語意的拷貝操作,是以當它執行完成之後,scoped_ptr智能指針p1引用的目标對象将會轉移到給scoped_ptr智能指針p2引用,也就是說,scoped_ptr智能指針p1自動放棄了對目标對象的引用。

      是以,通過調用scoped_ptr類的成員函數Pass,就能夠将一個scoped_ptr智能指針引用的目标對象轉移到另外一個scoped_ptr智能指針,也就是實作了scoped_ptr智能指針的move語意。此外,scoped_ptr類還提供了另外一個成員函數PassAs,它的作用與成員函數Pass是一樣的。

      接下來我們再看Chromium的弱智能指針的實作,它由類WeakPtr實作。我們首先給出WeakPtr類的關系圖,如圖1所示:   

Chromium和WebKit的智能指針實作原理分析

圖1 WeakPtr類關系圖

       WeakPtr類繼承于WeakPtrBase類。WeakPtrBase類有一個成員變量ref_,它指向一個WeakReference對象。WeakPtr類實作弱智能指針的功能的關鍵點就在于這個WeakReference對象,那麼這個WeakReference對象是怎麼來的呢?

       一個對象如果需要被弱智能指針引用,那麼它所屬的類就必須要繼承于SupportsWeakPtr類。SupportsWeakPtr類又是繼承于SupportsWeakPtrBase類的。SupportsWeakPtrBase類有一個靜态成員函數StaticAsWeakPtr,用來将一個間接繼承于SupportsWeakPtr類的子類對象封裝成一個WeakPtr弱智能指針。

       SupportsWeakPtr類有一個成員函數AsWeakPtr,用來将一個SupportsWeakPtr子類對象封裝成一個WeakPtr弱智能指針。在封裝的過程中,需要用到成員變量weak_reference_owner_指向的一個WeakReferenceeOwner對象。

       WeakReferenceeOwner類有一個成員變量flag_,它指向一個Flag對象。這個Flag對象有一個布爾類型的成員變量is_valid_。當一個WeakReferenceeOwner對象所屬的SupportsWeakPtr對象被銷毀時,也就是它的析構函數被調用的時候,這個WeakReferenceeOwner對象的成員函數Invalidate就會被調用。WeakReferenceeOwner類的成員函數Invalidate又會進一步調用成員變量flag_指向的一個Flag對象的成員函數Invalidate,将該Flag對象的成員變量is_valid_的值設定為false,表示一個相應的SupportsWeakPtr對象被銷毀了。

       當我們調用SupportsWeakPtr類的成員函數AsWeakPtr來獲得一個WeakPtr弱智能指針時,SupportsWeakPtr類的成員函數AsWeakPtr先會調用成員變量weak_reference_owner_指向的一個WeakReferenceeOwner對象的成員函數GetRef建立一個WeakReference對象,這個WeakReference對象通過成員變量flag_引用了上述的WeakReferenceeOwner對象的成員變量flag_指向的一個Flag對象。這意味在圖1中,WeakReferenceeOwner類和WeakReference對象的成員變量flag_指向的都是同一個Flag對象。

       這相當于就是通過一個共同的Flag對象将一個WeakPtr弱智能指針與它所引用的目标對象關聯起來,這樣我們需要使用一個WeakPtr弱智能指針所引用的目标對象時,就可以通過上述的共同Flag對象的成員函數IsValid來判斷目标對象是否已經被銷毀,實際上就是判斷該Flag對象的成員變量is_valid_是否等于true。

       為了更清楚地表達圖1所示的WeakPtr類關系圖,接下來我們通過代碼來分析該類關系圖中涉及到的每一個類。

       我們首先看SupportsWeakPtrBase類的實作,如下所示:

class SupportsWeakPtrBase {
 public:
  ......
  template<typename Derived>
  static WeakPtr<Derived> StaticAsWeakPtr(Derived* t) {
    ......
    return AsWeakPtrImpl<Derived>(t, *t);
  }

 private:
  ......
  template <typename Derived, typename Base>
  static WeakPtr<Derived> AsWeakPtrImpl(
      Derived* t, const SupportsWeakPtr<Base>&) {
    WeakPtr<Base> ptr = t->Base::AsWeakPtr();
    return WeakPtr<Derived>(ptr.ref_, static_cast<Derived*>(ptr.ptr_));
  }
};
           

       這個類定義在檔案external/chromium_org/base/memory/weak_ptr.h中。

       前面提到,一個類如果要配合WeakPtr弱智能指針使用,那麼該類必須要從SupportsWeakPtr繼承下來,如下所示:

class Base : public base::SupportsWeakPtr<Base> {};
           

       這時候如果有一個類Derived又繼承了Base類,如下所示:

class Derived : public Base {};
           

       這時候如果我們有一個Derived對象,并且想建立一個WeakPtr智能指針引用該Derived對象,那麼是不能調用從父類Base繼承下來的成員函數AsWeakPtr來建立的,如下所示:

Derived derived;
base::WeakPtr<Derived> ptr = derived.AsWeakPtr(); 
           

       這是因為SupportsWeakPtr<Base>類的成員函數AsWeakPtr傳回的是一個WeakPtr<Base>對象,并且這個WeakPtr<Base>對象不能自動轉換為一個WeakPtr<Derived>對象。

       為了能夠實作上述轉換,我們要使用到一個AsWeakPtr函數,它的實作如下所示:

template <typename Derived>
WeakPtr<Derived> AsWeakPtr(Derived* t) {
  return internal::SupportsWeakPtrBase::StaticAsWeakPtr<Derived>(t);
}
           

       這個函數定義在檔案external/chromium_org/base/memory/weak_ptr.h中。

       有了這個AsWeakPtr函數之後,就可以通過下面的代碼來建立一個WeakPtr<Derived>對象:

Derived derived;
base::WeakPtr<Derived> ptr = base::AsWeakPtr(&derived);
           

       函數AsWeakPtr調用了SupportsWeakPtrBase類的靜态成員函數StaticAsWeakPtr,後者又通過調用SupportsWeakPtrBase類的靜态成員函數AsWeakPtrImpl建立了一個WeakPtr<Derived>對象。

       SupportsWeakPtrBase類的靜态成員函數AsWeakPtrImpl首先是調用參數t描述的一個Derived對象從父類Base繼承下來的成員函數AsWeakPtr來建立一個WeakPtr<Base>對象。每一個WeakPtr<Base>對象都有一個成員變量ptr_,它儲存的就是WeakPtr<Base>對象所引用的目标對象的位址值。有了目标對象的位址值之後,就可以将它轉化一個Derived指針。這裡能夠轉換成功,是因為Derived類是繼承于Base類的。有了這個Derived指針之後,再結合前面獲得的WeakPtr<Base>對象的成員變量ref_r描述的一個WeakReference對象之後,就可以通過模闆類WeakPtr<Derived>的構造函數來建立一個WeakPtr<Derived>對象了,也就是建立一個引用了Derived對象的WeakPtr智能指針了。後面我們分析WeakPtr類的實作時,就會更清楚地了解上述過程。

      接着我們看SupportsWeakPtr類的實作,如下所示:

template <class T>
class SupportsWeakPtr : public internal::SupportsWeakPtrBase {
 public:
  ......

  WeakPtr<T> AsWeakPtr() {
    return WeakPtr<T>(weak_reference_owner_.GetRef(), static_cast<T*>(this));
  }

  ......

 private:
  internal::WeakReferenceOwner weak_reference_owner_;
  ......
};
           

      這個類定義在檔案external/chromium_org/base/memory/weak_ptr.h中。

      SupportsWeakPtr類最主要的就是通過成員變量weak_reference_owner_引用了一個WeakReferenceOwner對象。

      WeakReferenceOwner類的定義如下所示:

class BASE_EXPORT WeakReferenceOwner {
 public:
  ......
  ~WeakReferenceOwner();

  WeakReference GetRef() const;

  ......

  void Invalidate();

 private:
  mutable scoped_refptr<WeakReference::Flag> flag_;
};
           

       這個類定義在檔案external/chromium_org/base/memory/weak_ptr.h中。

       WeakReferenceOwner類最主要的就是通過成員變量flag_引用了一個Flag對象。注意,這個成員變量通過scoped_refptr智能指針來引用Flag對象。這意味着Flag類是具有引用計數功能的。

       Flag類的定義如下所示:

class BASE_EXPORT WeakReference {
 public:
  ......
  class BASE_EXPORT Flag : public RefCountedThreadSafe<Flag> {
   public:
    Flag();

    void Invalidate();
    bool IsValid() const;

   private:
    ......

    bool is_valid_;
  };

  ......
};
           

      這個類定義在檔案external/chromium_org/base/memory/weak_ptr.h中。

      Flag類繼承于RefCountedThreadSafe類,是以它就具有引用計數功能,并且在增加和減少引用計數時是線程安全的。

      現在我們分析前面列出的SupportsWeakPtr類的成員函數AsWeakPtr的實作,它首先是調用成員變量weak_reference_owner_指向的一個WeakReferenceOwner對象的成員函數GetRef獲得一個WeakReference對象,然後再通過WeakPtr類的構造函數建立一個WeakPtr弱智能指針。

      WeakReferenceOwner類的成員函數GetRef的實作如下所示:

WeakReference WeakReferenceOwner::GetRef() const {
  // If we hold the last reference to the Flag then create a new one.
  if (!HasRefs())
    flag_ = new WeakReference::Flag();

  return WeakReference(flag_.get());
}
           

       這個函數定義在檔案external/chromium_org/base/memory/weak_ptr.cc中。

       WeakReferenceOwner類的成員函數GetRef首先是調用成員函數HasRefs判斷成員變量flag_是否指向了一個Flag對象。如果不是,那麼就需要建立一個Flag對象,并且儲存在成員變量flag_中。

       WeakReferenceOwner類的成員函數GetRef接下來再以成員變量flag_指向的Flag對象為參數,建立一個WeakReference對象。

       WeakReference類的定義如下所示:

class BASE_EXPORT WeakReference {
 public:
  ......

  explicit WeakReference(const Flag* flag);
  
  ......

 private:
  scoped_refptr<const Flag> flag_;
};
           

       這個類定義在檔案external/chromium_org/base/memory/weak_ptr.h中。

       WeakReference類以const Flag*為參數的構造函數的實作如下所示:

WeakReference::WeakReference(const Flag* flag) : flag_(flag) {
}
           

       這個函數定義在檔案external/chromium_org/base/memory/weak_ptr.cc中。

       它主要就是用參數flag描述的一個Flag對象來初始化成員變量flag_描述的一個scoped_refptr智能指針。

       回到SupportsWeakPtr類的成員函數AsWeakPtr中,得到了一個WeakReference對象之後,就可以建立一個WeakPtr對象了。

       WeakPtr類的定義如下所示:

template <typename T>
class WeakPtr : public internal::WeakPtrBase {
 public:
  ......

  template <typename U>
  WeakPtr(const WeakPtr<U>& other) : WeakPtrBase(other), ptr_(other.ptr_) {
  }

  T* get() const { return ref_.is_valid() ? ptr_ : NULL; }

  T& operator*() const {
    ......
    return *get();
  }
  T* operator->() const {
    ......
    return get();
  }

  ......

 private:
  ......

  WeakPtr(const internal::WeakReference& ref, T* ptr)
      : WeakPtrBase(ref),
        ptr_(ptr) {
  }

  .....
  T* ptr_;
};
           

       這個類定義在檔案external/chromium_org/base/memory/weak_ptr.h中。

       從這裡就可以看到,WeakPtr類是從WeakPtrBase類繼承下來的,後者的定義如下所示:

class BASE_EXPORT WeakPtrBase {
 public:
  ......

 protected:
  explicit WeakPtrBase(const WeakReference& ref);

  WeakReference ref_;
};
           

      這個類定義在檔案external/chromium_org/base/memory/weak_ptr.h中。

      WeakPtrBase類主要就是通過成員變量ref_引用一個WeakReference對象。

      當我們調用WeakPtr類的構造函數将一個WeakReference對象和一個目标對象封裝成一個WeakPtr對象的時候,WeakPtr類的構造函數會調用父類WeakPtrBase的構造函數将上述WeakReference對象儲存在其成員變量ref_中,如下所示:

WeakPtrBase::WeakPtrBase(const WeakReference& ref) : ref_(ref) {
}
           

       這個函數定義在檔案external/chromium_org/base/memory/weak_ptr.cc中。

       再回到WeakPtr類以WeakReference對象和T對象指針為參數的構造函數中,它将WeakReference對象儲存在父類WeakPtrBase的成員變量ref_之後,還會将T對象指針儲存在自己的成員變量ptr_中,該成員變量描述的就是一個WeakPtr弱智能指針所引用的目标對象。

       這裡有一點需要注意的是,WeakPtr類的成員變量ptr_指向的T對象有可能是已經被銷毀了的,這是因為弱智能指針不能阻止它所引用的對象被銷毀。也就是說,當我們在使用一個WeakPtr弱智能指針的時候,它所引用的目标對象有可能是已經被銷毀的。我們可以通過WeakPtr類的成員函數get來判斷一個WeakPtr弱智能指針所引用的目标對象是否已經被銷毀。

       WeakPtr類的成員函數get調用從父類WeakPtrBase繼承下來的成員變量ref_指向的一個WeakReference對象的成員函數is_valid判斷一個WeakPtr弱智能指針所引用的目标對象是否已經被銷毀。如果還沒有被銷毀,那麼就說明WeakPtr類的成員變量ptr_描述的對象位址是有效的,是以就将它傳回調用者。如果已經被銷毀,那麼就傳回一個NULL值給調用者。

       WeakReference類的成員函數is_valid的實作如下所示:

bool WeakReference::is_valid() const { return flag_.get() && flag_->IsValid(); }
           

       這個函數定義在檔案external/chromium_org/base/memory/weak_ptr.cc中。

       WeakReference類的成員函數is_valid首先判斷成員變量flag_是否指向了一個Flag對象。如果指向了一個Flag對象,那麼就調用它的成員函數IsValid判斷它所關聯的一個對象是否還存活。

       Flag類的成員函數IsValid的實作如下所示:

bool WeakReference::Flag::IsValid() const {
  ......
  return is_valid_;
}
           

       這個函數定義在檔案external/chromium_org/base/memory/weak_ptr.cc中。

       Flag類的成員函數IsValid直接将成員變量is_valid_的值傳回給調用者。

       從前面分析的WeakReferenceOwner類的成員函數GetRef可以知道,上述的Flag對象即為目标對象通過WeakReferenceOwner類的關聯的那個Flag對象,這個Flag對象在建立的時候,它的成員變量is_valid_被設定為true,如下所示:

WeakReference::Flag::Flag() : is_valid_(true) {
  ......
}
           

       這個函數定義在檔案external/chromium_org/base/memory/weak_ptr.cc中。

       當一個Flag對象的宿主WeakReferenceOwner對象被析構時,就會将它的成員變量is_valid_的值設定為false,如下所示:

WeakReferenceOwner::~WeakReferenceOwner() {
  Invalidate();
}
           

       這個函數定義在檔案external/chromium_org/base/memory/weak_ptr.cc中。

       WeakReferenceOwner類的析構函數調用了另外一個成員函數Invalidate,它的實作如下所示:

void WeakReferenceOwner::Invalidate() {
  if (flag_.get()) {
    flag_->Invalidate();
    flag_ = NULL;
  }
}
           

       這個函數定義在檔案external/chromium_org/base/memory/weak_ptr.cc中。

       WeakReferenceOwner類的成員函數Invalidate首先檢查成員變量flag_是否指向了一個Flag對象。如果指向了,那麼就調用該Flag對象的成員函數Invalidate将它的成員變量is_valid_的值設定為false,如下所示:

void WeakReference::Flag::Invalidate() {
  ......
  is_valid_ = false;
}
           

       這個函數定義在檔案external/chromium_org/base/memory/weak_ptr.cc中。

       那麼,一個WeakReferenceOwner對象什麼時候會被析構呢?前面提到,SupportsWeakPtr類有一個成員變量weak_reference_owner_描述的是一個WeakReferenceOwner對象。這意味着當一個SupportsWeakPtr對象被析構時,它的成員變量weak_reference_owner_描述的是一個WeakReferenceOwner對象也會随之析構。

       SupportsWeakPtr類是作為一個被WeakPtr弱智能指針引用的目标對象的父類的,是以當一個WeakPtr弱智能指針引用的目标對象被析構時,目标對象的父類SupportsWeakPtr有的成員變量weak_reference_owner_描述的是一個WeakReferenceOwner對象就會被析構,随後又會将與該WeakReferenceOwner對象關聯的Flag對象的成員變量is_valid_的值設定為true,最後WeakPtr弱智能指針就可以通過該Flag對象的成員函數IsValid判斷出它所引用的目标對象是否已經被銷毀,進而就實作它作為弱智能指針的作用。

       以上就是Chromium的弱智能指針的實作原理。與WebKit的弱智能指針的實作原理相比,兩者都是通過為目标對象關聯一個額外的對象來實作的。這個額外的對象的生命周期比目标對象長,也就是說,即使目标對象不存在,該額外的對象也可以繼續存在,直到所有引用了該目标對象的弱智能指針都超出其生命周期範圍之後。不過兩者也有不同的地方,例如,Chromium的弱智能指針是通過繼承方式關聯額外的對象的,而WebKit的弱智指針是通過組合方式關聯額外的對象的。

       至此我們也分析完成了Chromium的智能指針的實作。最後結合Android系統的智能指針(輕量級指針、強指針和弱指針)的實作原理分析這篇文章,我們總結一下WebKit、Chromium和Android的智能指針的實作的主要相同點與不同點:

       1. WebKit和Chromium的智能指針分别提供了線程不安全和線程安全兩個版本的具有引用計數功能的基類RefCounted和ThreadSafeRefCounted。這樣我們在設計一個類的時候,就需要考慮這個類的對象是否會在多線程環境下被通路。如果是的話,那麼需要選擇ThreadSafeRefCounted作為基類。否則的話,選擇RefCounted作為基類就可以了。由于RefCounted在增加和減少引用計數時,不需要執行原子操作,是以效率就會更高。Android的智能指針隻提供了線程安全版本的具有引用計數功能的基類RefBase和LightRefBase。其中,繼承了RefBase類的子類的對象可以配合強指針和弱指針使用,而繼承了LightRefBase類的子類的對象隻可以配合輕量級指針使用。

       2. WebKit和Chromium同時提供了引用計數版本和非引用計數版本的智能指針的實作,即RefPtr和scoped_refptr,以及OwnPtr和scoped_ptr。Android沒有提供非引用計數版本的智能指針的實作。

       3. WebKit優化了智能指針作為函數參數和函數傳回值傳遞目标對象的過程,避免對目标對象的引用計數進行頻繁的加1和減1操作,這是通過PassRefPtr類和PassOwnPtr類實作的。Chromium和Android沒有提供此類智能指針的實作。

       4. WebKit和Chromium的智能指針都實作了move語意,但是Android的智能指針沒有實作move語意。

       5. Chromium和Android的弱智能指針是通過繼承方式關聯一個生命周期比目标對象更長的額外對象來實作的,而WebKit的弱智能指針則是通過組合方式關聯一個生命周期比目标對象更長的額外對象來實作的。

       6. WebKit和Chromium在實作一個類的時候,可以選擇是否需要同時支援強智能指針和弱智能指針,但是Android在實作一個類的時候,對強智能指針和弱智能指針的支援是整體的,也就是要麼同時支援強智能指針和弱智能指針,要麼同時不支援強智能指針和弱智能指針。

       從上面這6點總結就可以看出,WebKit和Chromium的智能指針設計更加精細化群組件化,開發者在設計一個類的時候,可以根據需要有選擇地實作相應的智能指針功能,而Android的智能指針設計相對來說就比較粗略,沒有太多可選擇的餘地。

       在Chromium中,與智能指針一樣作為重要的基礎設施的,還有多線程程式設計模型。Chromium的多線程程式設計模型用到了一種稱為Callback的機制線上程間進行通信,是以在接下來的兩篇文章中,我們先分析Chromium的Callback機制,然後再分析Chromium的線程程式設計模型,敬請關注!更多的資訊也可以關注老羅的新浪微網誌:http://weibo.com/shengyangluo。