Chromium和WebKit的智能指针实现原理分析_第1页
Chromium和WebKit的智能指针实现原理分析_第2页
Chromium和WebKit的智能指针实现原理分析_第3页
Chromium和WebKit的智能指针实现原理分析_第4页
Chromium和WebKit的智能指针实现原理分析_第5页
已阅读5页,还剩41页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

Chromium和WebKit的智能指针实现原理分析C++不像Java一样,由虚拟机负责对象分配和释放。也就是说,开发人员使用C++编写代码时,要自己负责对象分配和释放。WebKit和Chromium都是使用C++开发的,因此它们也面临上述问题。在解决对象释放问题时,要做到在对象不需要时自动释放,因为手动释放会带来忘记释放或者释放后又继续使用的隐患。智能指针是实现对象自动释放的有效技术手段。本文就分析Chromium和WebKit的智能指针的实现。在现实中,只要是稍微复杂一点的C++程序,都是不可能不使用智能指针的,因此智能指针是C++程序的一个最基本的设施。例如,Android系统的NativeFramework的各个模块中,几乎都可以看到sp和wp相关的代码。从前面一文可以知道,sp和wp就是Android系统提供的智能指针模块类。其中,前者用来实现强引用,而后者用来实现弱引用。在广义上,智能指针划分为两类。第一类智能指针引用的对象是可以共享的,也就是一个对象可以同时被多个智能指针引用。这类智能指针要求被引用的对象具有计数的功能,数值的大小就表示它目前被多少个智能指针引用。当一个对象的引用计数值等于0的时候,就表示它要被释放了。这类智能指针适合作为函数参数或者返回值在模块之间进行传递,从而实现共享。第二类智能指针不要求被引用对象具有计数的功能。第二类智能指针引用的对象是独占的,也就是一个对象同一时刻只可以被一个智能指针引用。这类智能指针不要求被引用对象具有计数的功能。只要这类智能指针的生命周期超出了它自己的范围,那么它引用的对象就会被自动销毁。这类智能指针适合在函数或者内部使用,用来自动化释放那些不需要了的对象。WebKit和Chromium都同时提供了上述两种类型的智能指针的实现。此外,WebKit和Chromium还提供了弱智能指针。所谓弱智能指针,就是它们的存在不会影响到被引用对象的生命周期。它们适合用来解决对象之间存在循环引用时的释放问题。这一点我们在前面一文有描述,这里就不再累述。接下来,我们先分析WebKit的智能指针的实现,接着再分析Chromium的智能指针的实现,最后总结它们与Android系统实现的智能指针的区别,这样我们就能够对智能指针的实现有更深刻的理解。WebKit的第一类智能指针由类RefPtr实现。由于它要求被引用对象具有计数功能,因此就提供了一个具有计数功能的基类RefCounted。当一个对象可以被类RefPtr描述的对象引用时,它就必须要从基类RefCounted继承下来。RefCounted是一个模板类,为了减少编译时的代码膨胀,RefCounted类是从另外一个非模板类RefCountedBase继承下来的。RefCountedBase类才是负责提供计数功能的基类,它的实现如下所示:[cpp]viewplaincopyclassWTF_EXPORTRefCountedBase{public:voidref(){......++m_refCount;}......protected:RefCountedBase():m_refCount(1)......{}......boolderefBase(){......--m_refCount;if(!m_refCount){......returntrue;}......returnfalse;}......private:......intm_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类的实现如下所示:[cpp]viewplaincopytemplate<typenameT>classRefCounted:publicRefCountedBase{......public:voidderef(){if(derefBase())deletestatic_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类的实现如下所示:[cpp]viewplaincopyclassWTF_EXPORTThreadSafeRefCountedBase{......public:......voidref(){atomicIncrement(&m_refCount);}......protected:......boolderefBase(){......if(atomicDecrement(&m_refCount)<=0){......returntrue;}returnfalse;}private:intm_refCount;};这个函数定义在文件external/chromium_org/third_party/WebKit/Source/wtf/ThreadSafeRefCounted.h。与RefCountedBase类一样,ThreadSafeRefCountedBase类也实现了成员函数ref和derefBase,不过它通过原子操作atomicIncrement和atomicDecrement来分别增加和减少目标对象的引用计数,因此它是线程安全的。ThreadSafeRefCounted类的实现如下所示:[cpp]viewplaincopytemplate<classT>classThreadSafeRefCounted:publicThreadSafeRefCountedBase{public:voidderef(){if(derefBase())deletestatic_cast<T*>(this);}......};这个函数定义在文件external/chromium_org/third_party/WebKit/Source/wtf/ThreadSafeRefCounted.h。与RefCounted类一样,ThreadSafeRefCounted类也实现了成员函数deref,并且调用父类ThreadSafeRefCountedBase的成员函数derefBase减少对象的引用计数。当对象的引用计数小于等于0时,就会将它delete掉。有了RefCounted和ThreadSafeRefCounted这两个基类为对象提供计数功能之后,我们就继续分析第一类智能指针RefPtr的实现,如下所示:[cpp]viewplaincopynamespaceWTF{......template<typenameT>classRefPtr{......public:.......ALWAYS_INLINERefPtr(T*ptr):m_ptr(ptr){refIfNotNull(ptr);}......ALWAYS_INLINEexplicitRefPtr(T&ref):m_ptr(&ref){m_ptr->ref();}ALWAYS_INLINERefPtr(constRefPtr&o):m_ptr(o.m_ptr){refIfNotNull(m_ptr);}......ALWAYS_INLINE~RefPtr(){derefIfNotNull(m_ptr);}ALWAYS_INLINET*get()const{returnm_ptr;}......T&operator*()const{return*m_ptr;}ALWAYS_INLINET*operator->()const{returnm_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的实现如下所示:[cpp]viewplaincopynamespaceWTF{......template<typenameT>ALWAYS_INLINEvoidrefIfNotNull(T*ptr){if(LIKELY(ptr!=0)){......ptr->ref();}}template<typenameT>ALWAYS_INLINEvoidderefIfNotNull(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的引用计数操作是否是必须的呢?我们可以看下面的例子:[cpp]viewplaincopyRefPtr<T2>foo(RefPtr<T1>p2){RefPtr<T2>p3=newT2();......returnp3;}RefPtr<T1>p1=newT1();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,它的实现如下所示:[cpp]viewplaincopynamespaceWTF{......template<typenameT>classPassRefPtr{......public:PassRefPtr():m_ptr(0){}PassRefPtr(std::nullptr_t):m_ptr(0){}PassRefPtr(T*ptr):m_ptr(ptr){refIfNotNull(ptr);}......explicitPassRefPtr(T&ptr):m_ptr(&ptr){m_ptr->ref();}......PassRefPtr(constPassRefPtr&o):m_ptr(o.leakRef()){}......ALWAYS_INLINE~PassRefPtr(){derefIfNotNull(m_ptr);}template<typenameU>PassRefPtr(constRefPtr<U>&,EnsurePtrConvertibleArgDecl(U,T));T*get()const{returnm_ptr;}T*leakRef()constWARN_UNUSED_RETURN;T&operator*()const{return*m_ptr;}T*operator->()const{returnm_ptr;}......private:......mutableT*m_ptr;};template<typenameT>template<typenameU>inlinePassRefPtr<T>::PassRefPtr(constRefPtr<U>&o,EnsurePtrConvertibleArgDefn(U,T)):m_ptr(o.get()){T*ptr=m_ptr;refIfNotNull(ptr);}template<typenameT>inlineT*PassRefPtr<T>::leakRef()const{T*ptr=m_ptr;m_ptr=0;returnptr;}......};这个类定义在文件external/chromium_org/third_party/WebKit/Source/wtf/PassRefPtr.h中。注意,这里的模板参数T同样要么是从RefCounted的子类,要么是ThreadSafeRefCounted的子类。从PassRefPtr类的构造函数可以知道,当它们的参数是T*、T&和constRefPtr<T>&时,PassRefPtr类描述的智能指针与RefPtr类描述的智能指针并没有什么区别。但是当参数为constPassRefPtr<T>&时,也就是我们是一个PassRefPtr智能指针p1构造另一个PassRefPtr智能指针p2时,或者说将一个PassRefPtr智能指针p1传递给另一个PassRefPtr智能指针p2时,神奇的事情就发生了!智能指针p1就自动失去了对目标对象的引用,并且这时候目标对象的引用计数没有发生变化。这是通过PassRefPtr类的成员函数leakRef实现的。有了PassRefPtr类之后,我们再来改写上面举的例子,如下所示:[cpp]viewplaincopyPassRefPtr<T2>foo(PassRefPtr<T1>p2){PassRefPtr<T2>p3=newT2();......returnp3;}PassRefPtr<T1>p1=newT1();PassRefPtr<T2>p4=foo(p1);这时候很明显,T1对象和T2对象在函数foo的调用或者返回前后,它们的引用计数值都始终保持为1,只有当智能指针p1和智能指针p4超出其生命周期范围时,T1对象和T2对象的引用计数才会减少为0,从而被释放。这样就可以免去智能指针作为参数或者返回值传递目标对象的增加和减少引用计数操作,从而去掉无用功。从前面PassRefPtr类的定义可以知道,我们可以将一个RefPtr智能指针传递给一个PassRefPtr智能指针,这时候目标对象的引用计数会增加1。我们还可以另外一种方式将一个RefPtr智能指针传递给一个PassRefPtr智能指针。这种传递方式将会使得目标对象的引用计数不发生变化,并且RefPtr智能指针自动失去对目标对象的引用。这是通过调用RefPtr类的成员函数release实现的,如下所示:[cpp]viewplaincopynamespaceWTF{......template<typenameT>classRefPtr{......public:......PassRefPtr<T>release(){PassRefPtr<T>tmp=adoptRef(m_ptr);m_ptr=0;returntmp;}......private:T*m_ptr;};......};这个函数定义在文件external/chromium_org/third_party/WebKit/Source/wtf/RefPtr.h中。RefPtr类的成员函数release调用函数adoptRef来构造一个PassRefPtr智能指针,后者的实现如下所示:[cpp]viewplaincopynamespaceWTF{......template<typenameT>inlinePassRefPtr<T>adoptRef(T*p){......returnPassRefPtr<T>(p,PassRefPtr<T>::AdoptRef);}......};这个函数定义在文件external/chromium_org/third_party/WebKit/Source/wtf/PassRefPtr.h中。函数adoptRef调用以下的PassRefPtr类的构造函数来构造一个PassRefPtr智能指针,如下所示:[cpp]viewplaincopynamespaceWTF{......template<typenameT>classPassRefPtr{......private:enumAdoptRefTag{AdoptRef};PassRefPtr(T*ptr,AdoptRefTag):m_ptr(ptr){}......mutableT*m_ptr;};......};这个函数定义在文件external/chromium_org/third_party/WebKit/Source/wtf/PassRefPtr.h中。从这里就可以看到,上述的PassRefPtr类的构造函数并没有增加目标对象的引用计数,因此我们就可以在不改变目标对象的引用计数的前提下,将一个RefPtr智能指针传递给一个PassRefPtr智能指针,相当于就是将一个RefPtr智能指针对目标对象的所有权转移给一个PassRefPtr智能指针。接下来我们再看WebKit的第二类智能指针的实现,它由类OwnPtr实现,如下所示:[cpp]viewplaincopynamespaceWTF{.......template<typenameT>classOwnPtr{......public:typedeftypenameRemoveExtent<T>::TypeValueType;typedefValueType*PtrType;OwnPtr():m_ptr(0){}OwnPtr(std::nullptr_t):m_ptr(0){}......OwnPtr(constPassOwnPtr<T>&);......#if!COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES)......OwnPtr(constOwnPtr&);#endif~OwnPtr(){OwnedPtrDeleter<T>::deletePtr(m_ptr);m_ptr=0;}......PtrTypeget()const{returnm_ptr;}......PassOwnPtr<T>release();PtrTypeleakPtr()WARN_UNUSED_RETURN;ValueType&operator*()const{ASSERT(m_ptr);return*m_ptr;}PtrTypeoperator->()const{ASSERT(m_ptr);returnm_ptr;}......OwnPtr&operator=(constPassOwnPtr<T>&);......#ifCOMPILER_SUPPORTS(CXX_RVALUE_REFERENCES)OwnPtr(OwnPtr&&);......OwnPtr&operator=(OwnPtr&&);......#endif......private:#if!COMPILER_SUPPORTS(CXX_RVALUE_REFERENCES)//Ifrvaluereferencesaresupported,noncopyabletakescareofthis.OwnPtr&operator=(constOwnPtr&);#endif......PtrTypem_ptr;};......template<typenameT>inlineOwnPtr<T>::OwnPtr(constPassOwnPtr<T>&o):m_ptr(o.leakPtr()){}......template<typenameT>inlinePassOwnPtr<T>OwnPtr<T>::release(){PtrTypeptr=m_ptr;m_ptr=0;returnPassOwnPtr<T>(ptr);}template<typenameT>inlinetypenameOwnPtr<T>::PtrTypeOwnPtr<T>::leakPtr(){PtrTypeptr=m_ptr;m_ptr=0;returnptr;}......template<typenameT>template<typenameU>inlineOwnPtr<T>&OwnPtr<T>::operator=(constPassOwnPtr<U>&o){......PtrTypeptr=m_ptr;m_ptr=o.leakPtr();ASSERT(!ptr||m_ptr!=ptr);OwnedPtrDeleter<T>::deletePtr(ptr);return*this;}......#ifCOMPILER_SUPPORTS(CXX_RVALUE_REFERENCES)template<typenameT>inlineOwnPtr<T>::OwnPtr(OwnPtr<T>&&o):m_ptr(o.leakPtr()){}......template<typenameT>inlineOwnPtr<T>&OwnPtr<T>::operator=(OwnPtr<T>&&o){PtrTypeptr=m_ptr;m_ptr=o.leakPtr();ASSERT(!ptr||m_ptr!=ptr);OwnedPtrDeleter<T>::deletePtr(ptr);return*this;}......#endif......}//namespaceWTF这个类定义在文件external/chromium_org/third_party/WebKit/Source/wtf/OwnPtr.h中。从OwnPtr类的定义可以看到,OwnPtr智能指针只能通过以下构造函数创建:[cpp]viewplaincopytemplate<typenameT>inlineOwnPtr<T>::OwnPtr(constPassOwnPtr<T>&o):m_ptr(o.leakPtr()){}也就是说,我们创建了一个T对象后,只能先用来创建一个PassOwnPtr对象,然后再用该PassOwnPtr对象来创建一个OwnPtr智能指针。当然,我们也可以使用OwnPtr类的以下构造函数来创建一个没有引用任何目标对象的OwnPtr智能指针:[cpp]viewplaincopyOwnPtr():m_ptr(0){}OwnPtr(std::nullptr_t):m_ptr(0){}然后,再通过调用操作符重载函数operator=将一个PassOwnPtr对象赋值给OwnPtr智能指针:[cpp]viewplaincopytemplate<typenameT>template<typenameU>inlineOwnPtr<T>&OwnPtr<T>::operator=(constPassOwnPtr<U>&o){......PtrTypeptr=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来实现,如下所示:[cpp]viewplaincopytemplate<typenameT>inlinePassOwnPtr<T>OwnPtr<T>::release(){PtrTypeptr=m_ptr;m_ptr=0;returnPassOwnPtr<T>(ptr);}template<typenameT>inlinetypenameOwnPtr<T>::PtrTypeOwnPtr<T>::leakPtr(){PtrTypeptr=m_ptr;m_ptr=0;returnptr;}OwnPtr类的成员函数release将OwnPtr智能指针放弃引用的目标对象转移给了一个PassOwnPtr智能指针,这意味着可以将一个OwnPtr智能指针转化为一个PassOwnPtr指针,从而使得它引用的目标对象可以作为函数参数或者函数返回值传递。OwnPtr类的成员函数leakPtr是直接放弃OwnPtr智能指针引用的目标对象,不过它会返回该目标对象的地址值给调用者,也就是将一个指向目标对象的裸指针返回给调用者。我们注意到,OwnPtr类的成员函数leakPtr的返回值类型为OwnPtr<T>::PtrType,它的定义如下所示:[cpp]viewplaincopytypedeftypenameRemoveExtent<T>::TypeValueType;typedefValueType*PtrType;RemoveExtent是一个与C++11引入的右值引用(RvalueReference)有关一个模板类。右值引用通过T&&符号来表示,它主要是用来实现move语意和完美转发(perfectforwarding)。关于右值引用的概念,由于篇幅关系,这里不进行展开,然后强烈建议大家去看看这两篇文章。这里我们只需要知道,ValueType描述的是模板参数T的值类型,而PtrType描述的是模板参数T的裸指针类型,不管我们是使用T、T&还是T&&来推导模板参数。关于右值引用的move语意,这里值得再解释一下。简单地说,move语意是为了消除临时对象之间的拷贝操作,例如,对于以下表达式来说:[cpp]viewplaincopystrings=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赋值操作符函数,如下所示:[cpp]viewplaincopy#ifCOMPILER_SUPPORTS(CXX_RVALUE_REFERENCES)template<typenameT>inlineOwnPtr<T>::OwnPtr(OwnPtr<T>&&o):m_ptr(o.leakPtr()){}......template<typenameT>inlineOwnPtr<T>&OwnPtr<T>::operator=(OwnPtr<T>&&o){PtrTypeptr=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->来直接操作目标对象,例如调用目标对象的成员函数,如下所示:[cpp]viewplaincopyValueType&operator*()const{ASSERT(m_ptr);return*m_ptr;}PtrTypeoperator->()const{ASSERT(m_ptr);returnm_ptr;}当一个OwnPtr智能指针超出其生命周期范围内时,它所引用的对象就会被释放掉,如下所示:[cpp]viewplaincopy~OwnPtr(){OwnedPtrDeleter<T>::deletePtr(m_ptr);m_ptr=0;}从而就可以起到自动释放不再需要的对象的作用。从这里可以看到,OwnPtr智能指针是通过模板类OwnedPtrDeleter<T>的成员函数deletePtr来释放目标对象的,它的实现如下所示:[cpp]viewplaincopytemplate<typenameT>structOwnedPtrDeleter{staticvoiddeletePtr(T*ptr){COMPILE_ASSERT(!IsRefCounted<T>::value,UseRefPtrForRefCountedObjects);COMPILE_ASSERT(sizeof(T)>0,TypeMustBeComplete);deleteptr;}};这个函数定义在文件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的定义如下所示:[cpp]viewplaincopytemplate<typenameT>structIsRefCounted{staticconstboolvalue=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的定义如下所示:[cpp]viewplaincopynamespaceWTF{......template<typenameT,typenameU>classIsSubclass{typedefcharYesType;structNoType{charpadding[8];};staticYesTypesubclassCheck(U*);staticNoTypesubclassCheck(...);staticT*t;public:staticconstboolvalue=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智能指针引用的对象也可以作为函数参数或者函数返回值传递,它的定义如下所示:[cpp]viewplaincopynamespaceWTF{......template<typenameT>classPassOwnPtr{......public:typedeftypenameRemoveExtent<T>::TypeValueType;typedefValueType*PtrType;PassOwnPtr():m_ptr(0){}PassOwnPtr(std::nullptr_t):m_ptr(0){}......PassOwnPtr(constPassOwnPtr&o):m_ptr(o.leakPtr()){}......~PassOwnPtr(){OwnedPtrDeleter<T>::deletePtr(m_ptr);}PtrTypeget()const{returnm_ptr;}PtrTypeleakPtr()constWARN_UNUSED_RETURN;ValueType&operator*()const{ASSERT(m_ptr);return*m_ptr;}PtrTypeoperator->()const{ASSERT(m_ptr);returnm_ptr;}......template<typenameU>friendPassOwnPtr<U>adoptPtr(U*);......private:explicitPassOwnPtr(PtrTypeptr):m_ptr(ptr){}......mutablePtrTypem_ptr;};......template<typenameT>inlinetypenamePassOwnPtr<T>::PtrTypePassOwnPtr<T>::leakPtr()const{PtrTypeptr=m_ptr;m_ptr=0;returnptr;}......template<typenameT>inlinePassOwnPtr<T>adoptPtr(T*ptr){returnPassOwnPtr<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类实现,如下所示:[cpp]viewplaincopytemplate<typenameT>classWeakPtr{......public:......WeakPtr(PassRefPtr<WeakReference<T>>ref):m_ref(ref){}T*get()const{returnm_ref?m_ref->get():0;}voidclear(){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类的实现如下所示:[cpp]viewplaincopytemplate<typenameT>classWeakReference:publicThreadSafeRefCounted<WeakReference<T>>{......public:staticPassRefPtr<WeakReference<T>>create(T*ptr){returnadoptRef(newWeakReference(ptr));}staticPassRefPtr<WeakReference<T>>createUnbound(){returnadoptRef(newWeakReference());}T*get()const{......returnm_ptr;}voidclear(){......m_ptr=0;}voidbindTo(T*ptr){......m_ptr=ptr;}private:WeakReference():m_ptr(0){}explicitWeakReference(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类所示:[cpp]viewplaincopyclassHTMLDocumentParser:publicScriptableDocumentParser,privateHTMLScriptRunnerHost{......protected:......HTMLDocumentParser(HTMLDocument&,boolreportErrors);......private:......WeakPtrFactory<HTMLDocumentParser>m_weakFactory;......};这个类定义在文件external/chromium_org/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.h中。在创建一个HTMLDocumentParser对象的时候,它的成员变量m_weakFactory就会被初始化,如下所示:[cpp]viewplaincopyHTMLDocumentParser::HTMLDocumentParser(HTMLDocument&document,boolreportErrors):......,m_weakFactory(this)......{......}这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp中。WeakPtrFactory类的定义如下所示:[cpp]viewplaincopytemplate<typenameT>classWeakPtrFactory{......public:explicitWeakPtrFactory(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();}//WeshouldconsiderhavingcreateWeakPtrpopulatem_refthefirsttimecreateWeakPtriscalled.WeakPtr<T>createWeakPtr(){returnWeakPtr<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对象也会随之销毁,而

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论