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

http://qiusuoge.com/12108.html

探索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


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值