现在的位置: 首页 > 自动控制 > 工业·编程 > 正文

探索WebKit内核(五):智能指针解析

2014-08-31 05:30 工业·编程 ⁄ 共 4070字 ⁄ 字号 暂无评论

本文将从WebKit源码中解析WebKit智能指针的用法。进入正题之前,先还是要仔细看看官方文档。不管能否看明白还是要先看看这篇文章,毕竟这是本文最主要的参考文档。

文档里已提到2005之前,WebKit基于RefCounted来管理对象的销毁。

RefCounted

RefCounted原理很简单,就是最经典的引用计数的方式。它的源码也很简单,看看它最重要的两个方法,ref和deref:

    void ref() 

    { 

#if CHECK_REF_COUNTED_LIFECYCLE 

        ASSERT(m_verifier.isSafeToUse()); 

        ASSERT(!m_deletionHasBegun); 

        ASSERT(!m_adoptionIsRequired); 

#endif 

        ++m_refCount; 

    } 

    void deref() 

    { 

        if (derefBase()) 

            delete static_cast<T*>(this); 

    } 

    // Returns whether the pointer should be freed or not. 

    bool derefBase() 

    { 

#if CHECK_REF_COUNTED_LIFECYCLE 

        ASSERT(m_verifier.isSafeToUse()); 

        ASSERT(!m_deletionHasBegun); 

        ASSERT(!m_adoptionIsRequired); 

#endif 

  

        ASSERT(m_refCount > 0); 

        if (m_refCount == 1) { 

#if CHECK_REF_COUNTED_LIFECYCLE 

            m_deletionHasBegun = true; 

#endif 

            return true; 

        } 

  

        --m_refCount; 

#if CHECK_REF_COUNTED_LIFECYCLE 

        // Stop thread verification when the ref goes to 1 because it 

        // is safe to be passed to another thread at this point. 

        if (m_refCount == 1) 

            m_verifier.setShared(false); 

#endif 

        return false; 

    } 

抛开一些状态的维护不看,它其实就是在调用ref时,内部计数器加1,调用deref时计数器减1,当减到1时,就自动delete。所以一句话,它就是通过内部计数器来判断外部的引用从而实现自动销毁对象。这种方法虽然实现简单,但造成了调用者的麻烦,比如文档里提到的例子:

class Document { 

    ... 

    Title* m_title; 

  

Document::Document() 

    : m_title(0) 

  

Document::~Document() 

    if (m_title) 

        m_title->deref(); 

  

void Document::setTitle(Title* title) 

    if (title) 

        title->ref(); 

    if (m_title) 

        m_title->deref(); 

    m_title = title; 

简单的一个set方法,就需要来回调用ref和deref,在更复杂的场景下难免导致ref和deref的不对称,从而造成本该销毁却没销毁,或是错销毁的情况。后来,WebKit就引入了RefPtr, PassRefPtr, OwnPtr和PassOwnPtr来解决这个问题。

RefPtr和PassRefPtr

RefPtr的思路很简单,就是要把上面例子自动化,自动地在各项操作中加上deref和ref,先来看看它最关键几个方法的源码:

template<typename T> inline RefPtr<T>& RefPtr<T>::operator=(T* optr) 

    refIfNotNull(optr); 

    T* ptr = m_ptr; 

    m_ptr = optr; 

    derefIfNotNull(ptr); 

    return *this; 

  

ALWAYS_INLINE RefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); } 

看字面意思就能很清楚的知道,当把一个对象赋值给RefPtr包装过的对象后,它会先被赋值的对象ref,然后再给自己原来的对象deref,这实际上就是上例中setTitle的过程,所以改写后就极大简洁了代码:

class Document { 

    ... 

    RefPtr<Title> m_title; 

  

void Document::setTitle(Title* title) 

    m_title = title; 

但这虽然简洁了代码,但没有简洁代码实际的执行过程,所以文档里就提到了频繁ref和deref的问题,比如以下代码:

RefPtr<Node> createSpecialNode() 

    RefPtr<Node> a = new Node; 

    a->setSpecial(true); 

    return a; 

  

RefPtr<Node> b = createSpecialNode(); 

这段代码最终的结果是Node对象的引用为1,但结合RefPtr的源码,我们可知,其中会有多次来回的ref和deref,文档里也解释了这个过程。所以就需要一种机制来做到参数传递时可以附带传递引用值,而不是通过正负抵消的方式来保证引用的不变,这就是PassRefPtr存在的价值。现在看看PassRefPtr几个关键方法的源码:

template<typename T> inline PassRefPtr<T> adoptRef(T* p) 

    { 

        adopted(p); 

        return PassRefPtr<T>(p, true); 

    } 

  

PassRefPtr(T* ptr, bool) : m_ptr(ptr) { } 

  

PassRefPtr& operator=(const PassRefPtr&) { COMPILE_ASSERT(!sizeof(T*), PassRefPtr_should_never_be_assigned_to); return *this; } 

template<typename T> inline T* PassRefPtr<T>::leakRef() const 

    { 

        T* ptr = m_ptr; 

        m_ptr = 0; 

        return ptr; 

    } 

从中可以知道,PassRefPtr主要用于参数传递中,当传递完成后,被PassRefPtr包装的对象就会被销毁,并且整个过程中不改变对象引用。那么基于PassRefPtr重构上例的代码:

PassRefPtr<Node> Node::create() 

    return adoptRef(new Node); 

  

RefPtr<Node> e = Node::create(); 

最终效果就是Node的引用为1,并且中间没有引用的变化。但是,PassRefPtr是不能替代RefPtr的,因为被赋值后,它就是的NULL了,再调用就会有空指针的错误。所以它们俩的引用场景很明确:

RefPtr:用于希望能自动管理对象回收的地方。

PassRefPtr:用于方法参数和返回值参数上。

两者总是配合使用。

RefPtr和PassRefPtr都是从RefCounted演变而来,并且只能用于继承自RefCounted的对象,所以有一定的局限性,也就有了OwnPtr和PassOwnPtr用武之地。

OwnPtr和PassOwnPtr

OwnPtr不是基于计数来管理对象销毁,它简单又暴力,先看看它几个关键方法的源码:

template<typename T> template<typename U> inline OwnPtr<T>::OwnPtr(const PassOwnPtr<U>& o) 

    : m_ptr(o.leakPtr()) 

  

template<typename T> inline PassOwnPtr<T> OwnPtr<T>::release() 

    PtrType ptr = m_ptr; 

    m_ptr = 0; 

    return adoptPtr(ptr); 

  

template<typename T> inline OwnPtr<T>& OwnPtr<T>::operator=(const PassOwnPtr<T>& o) 

    PtrType ptr = m_ptr; 

    m_ptr = o.leakPtr(); 

    ASSERT(!ptr || m_ptr != ptr); 

    deleteOwnedPtr(ptr); 

    return *this; 

它的语义就是这个对象仅仅只能由我来管理,别人都不能引用,别人赋值给我,就自动赋值为NULL,仅我拥有此对象的引用,当我的作用域完了后,会自动销毁。它比较适合不是从RefCounted继承下来的对象,并且生命周期由我控制的场景。

好了,经过上面的分析,基本上把WebKit的智能指针的原理和使用场景搞清楚了。得到的启发是,C++内存管理固然复杂,但也有简单的方法来控制这个复杂的范围的。

作者:cutesource

给我留言

留言无头像?