C++不像Java一样,由虚拟机负责对象分配和释放。也就是说,开发人员使用C++编写代码时,要自己负责对象分配和释放。WebKit和Chromium都是使用C++开发的,因此它们也面临上述问题。在解决对象释放问题时,要做到在对象不需要时自动释放,因为手动释放会带来忘记释放或者释放后又继续使用的隐患。智能指针是实现对象自动释放的有效技术手段。本文就分析Chromium和WebKit的智能指针的实现。
在现实中,只要是稍微复杂一点的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
有了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
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
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
宏COMPILE_ASSERT实际上是利用C++ 11引入的static_assert特性来实现的。这个static_assert语句不会对编译后得到的二进制代码有任何影响,纯粹是编译期间使用的,用来在编译期间就能捕捉到错误。
从模板类OwnedPtrDeleter
模板类IsRefCounted
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是不是从RefCountedBase类或者ThreadSafeRefCountedBase类继承下来,是通过调用模板类IsSubclass
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
注意,sizeof是一个编译器运算符号,当它的参数是一个函数的时候,实际上计算的是该函数的返回值的类型所占据的字节数。由于函数的返回值类型通过声明就可以知道,因此模板类IsSubclass
前面提到,为了创建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类的实现是类似的,最主要的区别在于两点:
PassOwnPtr类提供了一个拷贝构造函数,通过此拷贝构造函数可以用一个PassOwnPtr智能指针构造另外一个PassOwnPtr智能指针,不过这样会导致原来的PassOwnPtr智能指针失去对目标对象的引用,因为目标对象转为被新构造的PassOwnPtr智能指针引用了。这意味着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类的实现也类似,不过有两点区别:
RefCountedThreadSafe类是从RefCountedThreadSafeBase类继承下来的,后者提供了线程安全版本的计数功能。
第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
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来实现。
类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。
在scoped_ptr类以const RValue&为参数的构造函数的调用过程中又会发生以下事情:
1. 参数rvalue描述的RValue对象的成员变量object描述的scoped_ptr智能指针(也就是scoped_ptr智能指针p1)的成员变量impl_将会用来初始化正在构造的智能指针(也就是scopedptr智能指针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所示:
图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对象有一个布尔类型的成员变量isvalid。当一个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
为了能够实现上述转换,我们要使用到一个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;
base::WeakPtr<Derived> ptr = base::AsWeakPtr(&derived);
函数AsWeakPtr调用了SupportsWeakPtrBase类的静态成员函数StaticAsWeakPtr,后者又通过调用SupportsWeakPtrBase类的静态成员函数AsWeakPtrImpl创建了一个WeakPtr
SupportsWeakPtrBase类的静态成员函数AsWeakPtrImpl首先是调用参数t描述的一个Derived对象从父类Base继承下来的成员函数AsWeakPtr来创建一个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类的子类的对象只可以配合轻量级指针使用。
WebKit和Chromium同时提供了引用计数版本和非引用计数版本的智能指针的实现,即RefPtr和scoped_refptr,以及OwnPtr和scoped_ptr。Android没有提供非引用计数版本的智能指针的实现。
WebKit优化了智能指针作为函数参数和函数返回值传递目标对象的过程,避免对目标对象的引用计数进行频繁的加1和减1操作,这是通过PassRefPtr类和PassOwnPtr类实现的。Chromium和Android没有提供此类智能指针的实现。
5. Chromium和Android的弱智能指针是通过继承方式关联一个生命周期比目标对象更长的额外对象来实现的,而WebKit的弱智能指针则是通过组合方式关联一个生命周期比目标对象更长的额外对象来实现的。
从上面这6点总结就可以看出,WebKit和Chromium的智能指针设计更加精细化和组件化,开发者在设计一个类的时候,可以根据需要有选择地实现相应的智能指针功能,而Android的智能指针设计相对来说就比较粗略,没有太多可选择的余地。
在Chromium中,与智能指针一样作为重要的基础设施的,还有多线程编程模型。Chromium的多线程编程模型用到了一种称为Callback的机制在线程间进行通信,因此在接下来的两篇文章中,我们先分析Chromium的Callback机制,然后再分析Chromium的线程编程模型,敬请关注!更多的信息也可以关注老罗的新浪微博:http://weibo.com/shengyangluo。
扫一扫
在手机上阅读