Chromium学习之路(一)----智能指针实现原理分析

       Chromium和WebKit的智能指针实现原理分析。

智能指针概述

       智能指针对于写c艹程序的开发人员来说并不陌生,尤其大一点的程序,也是司空见惯。(容我吐槽一下自己,接触webrtc中也有大量的智能指针,奈何我是个c艹新手)WebKit和Chromium都是C艹开发的,在解决对象释放问题时,要做到在对象不需要时自动释放,因为手动释放会带来忘记释放或者释放后又继续使用的隐患。智能指针是实现对象自动释放的有效技术手段。

       之前文章有一篇智能指针简单分析,在Android系统源码里用到了大量的sp和wp相关的代码。sp和wp就是android系统提供的智能指针模版类,前者用来实现强引用,后者用来实现弱饮用。

       引用一段对智能指针的描述:

       在广义上,智能指针划分为两类。第一类智能指针引用的对象是可以共享的,也就是一个对象可以同时被多个智能指针引用。这类智能指针要求被引用的对象具有计数的功能,数值的大小就表示它目前被多少个智能指针引用。当一个对象的引用计数值等于0的时候,就表示它要被释放了。这类智能指针适合作为函数参数或者返回值在模块之间进行传递,从而实现共享。第二类智能指针不要求被引用对象具有计数的功能。
       第二类智能指针引用的对象是独占的,也就是一个对象同一时刻只可以被一个智能指针引用。这类智能指针不要求被引用对象具有计数的功能。只要这类智能指针的生命周期超出了它自己的范围,那么它引用的对象就会被自动销毁。这类智能指针适合在函数或者内部使用,用来自动化释放那些不需要了的对象。

       Chromium和WebKit还提供了弱智能指针,就是他们的存在不会影响被引用对象的生命周期。他们适合来解决对象这件存在的循环引用问题。再次饮用一段介绍:

       在计算机科学领域中,提供垃圾收集(Garbage Collection)功能的系统框架,即提供对象托管功能的系统框架,例如Java应用程序框架,也是采用上述的引用计数技术方案来实现的,然而,简单的引用计数技术不能处理系统中对象间循环引用的情况。考虑这样的一个场景,系统中有两个对象A和B,在对象A的内部引用了对象B,而在对象B的内部也引用了对象A。当两个对象A和B都不再使用时,垃圾收集系统会发现无法回收这两个对象的所占据的内存的,因为系统一次只能收集一个对象,而无论系统决定要收回对象A还是要收回对象B时,都会发现这个对象被其它的对象所引用,因而就都回收不了,这样就造成了内存泄漏。这样,就要采取另外的一种引用计数技术了,即对象的引用计数同时存在强引用和弱引用两种计数,例如,Apple公司提出的Cocoa框架,当父对象要引用子对象时,就对子对象使用强引用计数技术,而当子对象要引用父对象时,就对父对象使用弱引用计数技术,而当垃圾收集系统执行对象回收工作时,只要发现对象的强引用计数为0,而不管它的弱引用计数是否为0,都可以回收这个对象,但是,如果我们只对一个对象持有弱引用计数,当我们要使用这个对象时,就不直接使用了,必须要把这个弱引用升级成为强引用时,才能使用这个对象,在转换的过程中,如果对象已经不存在,那么转换就失败了,这时候就说明这个对象已经被销毁了,不能再使用了。

       因此要分别分析WebKit和Chromium的智能指针,好在Android源码里面都有,都位于external/chromuim_org/下面,之前下过android源码就可以直接查看了。

WebKit智能指针

第一类智能指针

       WebKit第一类智能指针由RefPtr实现,它的基类是RefCounted,提供引用计数功能。当一个对象可以被类RefPtr描述的对象引用时,它就必须要从基类RefCounted继承下来。
       RefCounted是一个模板类,为了减少编译时的代码膨胀,RefCounted类是从另外一个非模板类RefCountedBase继承下来的。RefCountedBase类才是负责提供计数功能的基类,位于external/chromium_org/third_party/WebKit/Source/wtf/RefCounted.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
class WTF_EXPORT RefCountedBase {
public:
void ref()
{

#if CHECK_REF_COUNTED_LIFECYCLE
// Start thread verification as soon as the ref count gets to 2. This
// heuristic reflects the fact that items are often created on one thread
// and then given to another thread to be used.
// FIXME: Make this restriction tigher. Especially as we move to more
// common methods for sharing items across threads like CrossThreadCopier.h
// We should be able to add a "detachFromThread" method to make this explicit.
if (m_refCount == 1)
m_verifier.setShared(true);
// If this assert fires, it either indicates a thread safety issue or
// that the verification needs to change. See ThreadRestrictionVerifier for
// the different modes.
ASSERT(m_verifier.isSafeToUse());
ASSERT(!m_adoptionIsRequired);
#endif
ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun);
//这才是重点,饮用计数+1
++m_refCount;
}

bool hasOneRef() const
{

ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun);
#if CHECK_REF_COUNTED_LIFECYCLE
ASSERT(m_verifier.isSafeToUse());
#endif
return m_refCount == 1;
}

int refCount() const
{

#if CHECK_REF_COUNTED_LIFECYCLE
ASSERT(m_verifier.isSafeToUse());
#endif
return m_refCount;
}

protected:
RefCountedBase()
: m_refCount(1)//默认创建时候引用计数为1
#if SECURITY_ASSERT_ENABLED
, m_deletionHasBegun(false)
#endif
#if CHECK_REF_COUNTED_LIFECYCLE
, m_adoptionIsRequired(true)
#endif
{
}

~RefCountedBase()
{
ASSERT_WITH_SECURITY_IMPLICATION(m_deletionHasBegun);
#if CHECK_REF_COUNTED_LIFECYCLE
ASSERT(!m_adoptionIsRequired);
#endif
}

// Returns whether the pointer should be freed or not.
bool derefBase()
{

ASSERT_WITH_SECURITY_IMPLICATION(!m_deletionHasBegun);
#if CHECK_REF_COUNTED_LIFECYCLE
ASSERT(m_verifier.isSafeToUse());
ASSERT(!m_adoptionIsRequired);
#endif

ASSERT(m_refCount > 0);
//引用计数-1
--m_refCount;
//如果引用计数为0,就return true,为后面的删除对象作出判断
if (!m_refCount) {
#if SECURITY_ASSERT_ENABLED
m_deletionHasBegun = true;
#endif
return true;
}

#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;
}

#if CHECK_REF_COUNTED_LIFECYCLE
bool deletionHasBegun() const
{

return m_deletionHasBegun;
}
#endif

private:

#if CHECK_REF_COUNTED_LIFECYCLE || SECURITY_ASSERT_ENABLED
friend void adopted(RefCountedBase*);
#endif

int m_refCount;//引用计数,核心变量
#if SECURITY_ASSERT_ENABLED
bool m_deletionHasBegun;
#endif
#if CHECK_REF_COUNTED_LIFECYCLE
bool m_adoptionIsRequired;
ThreadRestrictionVerifier m_verifier;
#endif
};

#if CHECK_REF_COUNTED_LIFECYCLE || SECURITY_ASSERT_ENABLED
inline void adopted(RefCountedBase* object)
{

if (!object)
return;
ASSERT_WITH_SECURITY_IMPLICATION(!object->m_deletionHasBegun);
#if CHECK_REF_COUNTED_LIFECYCLE
object->m_adoptionIsRequired = false;
#endif
}
#endif

       忽略其他障眼法,能够看到RefCountedBase类有一个类型为int的成员变量m_refCount,它就是用来描述对象的引用计数的。此外,RefCountedBase类还提供了两个成员函数ref和derefBase,分别用来增加和减少成员变量m_refCount描述的对象引用计数。

       调用RefCountedBase类的成员函数derefBase减少一个对象的引用计数时,如果减少后的引用计数等于0,那么它的返回值就等于true,表示该对象应该被释放了,不过这个释放的操作留给子类RefCounted实现。

       从RefCountedBase类的构造函数可以看到,一个RefCountedBase类及其子类对象被创建出来的时候,它的引用计数值就已经被初始化为1,这意味着不需要额外调用RefCountedBase类的成员函数ref来增加新创建对象的引用计数。

       这是RefCounted的基类,提供了引用计数的基本实现,接下来就看看子类RefCounted,同样位于external/chromium_org/third_party/WebKit/Source/wtf/RefCounted.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
template<typename T> class RefCounted : public RefCountedBase {
WTF_MAKE_NONCOPYABLE(RefCounted);
WTF_MAKE_FAST_ALLOCATED;

public:
void deref()
{

//调用了父类的derefBase函数
if (derefBase())//如果返回true,就delete掉这个对象
delete static_cast<T*>(this);
}

protected:
#ifdef ENABLE_INSTANCE_COUNTER
RefCounted()
{
incrementInstanceCount<T>(static_cast<T*>(this));
}

~RefCounted()
{
decrementInstanceCount<T>(static_cast<T*>(this));
}
#else
RefCounted()
{
}
#endif
};

       RefCounted类有一个成员函数deref,它调用父类RefCountedBase类的成员函数derefBase减少对象的引用计数。当RefCountedBase类的成员函数derefBase的返回值等于true的时候,就表示对象该释放了,因此RefCounted类的成员函数deref就直接将它delete掉。

       RefCounted的基类RefCountedBase在增加或者减少引用计数的时候,并没有加锁或者执行原子加减操作。因此,我们就说RefCounted类是线程不安全的。如果我们需要线程安全版本的RefCounted类,WebKit提供了另外一个类ThreadSafeRefCounted,它继承于ThreadSafeRefCountedBase类。ThreadSafeRefCountedBase类位于external/chromium_org/third_party/WebKit/Source/wtf/ThreadSafeRefCounted.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
class WTF_EXPORT ThreadSafeRefCountedBase {
WTF_MAKE_NONCOPYABLE(ThreadSafeRefCountedBase);
WTF_MAKE_FAST_ALLOCATED;
public:
ThreadSafeRefCountedBase(int initialRefCount = 1)
: m_refCount(initialRefCount)
{
}

void ref()
{

//原子操作保证线程安全
atomicIncrement(&m_refCount);
}

bool hasOneRef()
{

return refCount() == 1;
}

int refCount() const
{

return static_cast<int const volatile &>(m_refCount);
}

protected:
// Returns whether the pointer should be freed or not.
bool derefBase()
{

WTF_ANNOTATE_HAPPENS_BEFORE(&m_refCount);
//原子操作保证线程安全
if (atomicDecrement(&m_refCount) <= 0) {
WTF_ANNOTATE_HAPPENS_AFTER(&m_refCount);
return true;
}
return false;
}

private:
int m_refCount;
};

       与RefCountedBase类一样,ThreadSafeRefCountedBase类也实现了成员函数ref和derefBase,不过它通过原子操作atomicIncrement和atomicDecrement来分别增加和减少目标对象的引用计数,因此它是线程安全的。 ThreadSafeRefCounted类的实现如下,同样位于external/chromium_org/third_party/WebKit/Source/wtf/ThreadSafeRefCounted.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
template<class T> class ThreadSafeRefCounted : public ThreadSafeRefCountedBase {
public:
void deref()
{

if (derefBase())
delete static_cast<T*>(this);
}

protected:
ThreadSafeRefCounted()
{
}
};

       与RefCounted类一样,ThreadSafeRefCounted类也实现了成员函数deref,并且调用父类ThreadSafeRefCountedBase的成员函数derefBase减少对象的引用计数。当对象的引用计数小于等于0时,就会将它delete掉。

       有了RefCounted和ThreadSafeRefCounted这两个基类为对象提供计数功能之后,我们就继续分析第一类智能指针RefPtr的实现,位于external/chromium_org/third_party/WebKit/Source/wtf/RefPtr.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
template<typename T> class RefPtr {
WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(RefPtr);
WTF_DISALLOW_ZERO_ASSIGNMENT(RefPtr);
public:
ALWAYS_INLINE RefPtr() : m_ptr(0) { }
ALWAYS_INLINE RefPtr(std::nullptr_t) : m_ptr(0) { }
ALWAYS_INLINE RefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); }
template<typename U> RefPtr(const RawPtr<U>& ptr, EnsurePtrConvertibleArgDecl(U, T)) : m_ptr(ptr.get()) { refIfNotNull(m_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); }
template<typename U> RefPtr(const RefPtr<U>& o, EnsurePtrConvertibleArgDecl(U, T)) : m_ptr(o.get()) { refIfNotNull(m_ptr); }

// See comments in PassRefPtr.h for an explanation of why this takes a const reference.
template<typename U> RefPtr(const PassRefPtr<U>&, EnsurePtrConvertibleArgDecl(U, T));

// Hash table deleted values, which are only constructed and never copied or destroyed.
RefPtr(HashTableDeletedValueType) : m_ptr(hashTableDeletedValue()) { }
bool isHashTableDeletedValue() const { return m_ptr == hashTableDeletedValue(); }

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

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

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

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

bool operator!() const { return !m_ptr; }

// This conversion operator allows implicit conversion to bool but not to other integer types.
typedef T* (RefPtr::*UnspecifiedBoolType);
operator UnspecifiedBoolType() const { return m_ptr ? &RefPtr::m_ptr : 0; }

RefPtr& operator=(const RefPtr&);
RefPtr& operator=(T*);
RefPtr& operator=(const PassRefPtr<T>&);
RefPtr& operator=(std::nullptr_t) { clear(); return *this; }

template<typename U> RefPtr<T>& operator=(const RefPtr<U>&);
template<typename U> RefPtr<T>& operator=(const PassRefPtr<U>&);
template<typename U> RefPtr<T>& operator=(const RawPtr<U>&);

void swap(RefPtr&);

static T* hashTableDeletedValue() { return reinterpret_cast<T*>(-1); }

private:
T* m_ptr;
};

       注意,这里的模板参数T要么是从RefCounted的子类,要么是ThreadSafeRefCounted的子类。

       除了参数类型为T&的构造函数之外,RefPtr类的所有其它构造函数都会调用函数refIfNotNull来增加目标对象的引用计数。在参数类型为T&的构造函数中,能够保证参数ref不为NULL,因此就可以直接调用目标对象的成员函数ref来增加其引用计数。

       相应地,RefPtr类的析构函数会调用函数derefIfNotNull来减少目标对象的引用计数。这样就可以保证一个智能指针超出其生命周期时,能够自动释放它引用的对象。

       为了能够通过智能指针来调用目标对象的成员函数,RefPtr类实现了两个操作符重载成员函数*和->。此外,RefPtr类还实现了一个成员函数get,用来获取一个指向目标对象的裸指针,这样就实现智能指针到裸指针的转换。

       函数refIfNotNull和derefIfNotNull的实现如下,位于external/chromium_org/third_party/WebKit/Source/wtf/PassRefPtr.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
template<typename T> ALWAYS_INLINE void refIfNotNull(T* ptr)
{

if (LIKELY(ptr != 0)) {
requireAdoption(ptr);
ptr->ref();
}
}

template<typename T> ALWAYS_INLINE void derefIfNotNull(T* ptr)
{

if (LIKELY(ptr != 0))
ptr->deref();
}

       函数refIfNotNull和derefIfNotNull分别能过调用参数ptr描述的对象的成员函数ref和deref减少其引用计数。不过在增加和减少参数ptr描述的对象的引用计数之前,会先判断参数ptr的值是否等于0。只有在不等于0的情况下,才会执行增加和减少引用计数的操作。

       以上就是WebKit的第一类智能指针RefPtr的实现。

去除无用引用计数

       在实际使用中,似乎没有什么问题。但是细心的同学可能会注意到,当智能指针作为参数或者返回值在函数之间传递时,都会执行一次加1的引用计数操作,而当参数或者返回值超出其生命周期时,又会对称地执行一次减1的引用计数操作。这些加1和减1的引用计数操作是否是必须的呢?我们可以看下面的例子:

1
2
3
4
5
6
7
8
9
10
RefPtr<T2> foo(RefPtr<T1> p2) {
RefPtr<T2> p3 = new T2();

//......

return p3;
}

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

       上述过程的引用计数计算如下:

  1. 我们先new一个T1对象,并且通过它来构造智能指针p1时,这时候T1对象的引用计数为1,它不会被销毁。
  2. 接下来我们以智能指针p1为参数调用函数foo,这时候智能指针p2也引用了T1对象,因此T1对象的引用计数增加为2,它也不会被销毁。
  3. 从函数foo返回后,智能指针p2超出了其生命周期范围,因此T1对象的引用计数减少为1,它仍然是不会被销毁。
  4. 要等到智能指针p1也超出其生命周期范围时,T1对象的引用计数才会减少为0,从而被释放。

       go on:

  1. 在函数foo内部,我们先new一个T2对象,并且通过它来构造智能指针p3,这时候T2对象的引用计数为1,它不会被销毁。
  2. 接下来函数foo返回智能指针p3给智能指针p4,这时候智能指针p4也引用了T2对象,因此T2对象的引用计数增加为2,它也不会被销毁。
  3. 与此同时,智能指针p3超出了其生命周期范围,因此T2对象的引用计数减少为1,它仍然是不会被销毁。
  4. 同样是要等到智能指针p4也超出其生命周期范围时,T2对象的引用计数才会减少为0,从而被释放。

       很明显,在调用函数foo期间,完全是没有增加和减少T1对象和T2对象的引用计数的,这相当于是做了两次无用功。而且在实际的工程实践中,上述的代码是相当普通的,因此就会充斥着大量的无用功操作。

       为了去除上述的无用功,WebKit提供了另外一个智能指针类PassRefPtr,这个类定义在文件external/chromium_org/third_party/WebKit/Source/wtf/PassRefPtr.h中,它的实现如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
   template<typename T> class PassRefPtr {
WTF_DISALLOW_CONSTRUCTION_FROM_ZERO(PassRefPtr);
public:
PassRefPtr() : m_ptr(0) { }
PassRefPtr(std::nullptr_t) : m_ptr(0) { }
PassRefPtr(T* ptr) : m_ptr(ptr) { refIfNotNull(ptr); }
template<typename U> PassRefPtr(const RawPtr<U>& ptr, EnsurePtrConvertibleArgDecl(U, T)) : m_ptr(ptr.get()) { refIfNotNull(m_ptr); }
explicit PassRefPtr(T& ptr) : m_ptr(&ptr) { m_ptr->ref(); }
// It somewhat breaks the type system to allow transfer of ownership out of
// a const PassRefPtr. However, it makes it much easier to work with PassRefPtr
// temporaries, and we don't have a need to use real const PassRefPtrs anyway.
//我们是一个PassRefPtr智能指针p1构造另一个PassRefPtr智能指针p2时,
//或者说将一个PassRefPtr智能指针p1传递给另一个PassRefPtr智能指针p2时,
//智能指针p1就自动失去了对目标对象的引用,
//并且这时候目标对象的引用计数没有发生变化。
PassRefPtr(const PassRefPtr& o) : m_ptr(o.leakRef()) { }
template<typename U> PassRefPtr(const PassRefPtr<U>& o, EnsurePtrConvertibleArgDecl(U, T)) : 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; }

bool operator!() const { return !m_ptr; }

// This conversion operator allows implicit conversion to bool but not to other integer types.
typedef T* (PassRefPtr::*UnspecifiedBoolType);
operator UnspecifiedBoolType() const { return m_ptr ? &PassRefPtr::m_ptr : 0; }

friend PassRefPtr adoptRef<T>(T*);

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

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

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);
}
//leakRef函数回抹去参数指针对象的引用,强行赋值为0(NULL)
//此时它就就自动失去了对目标对象的引用,但是用于构造另一个只能指针的refCount还不变
template<typename T> inline T* PassRefPtr<T>::leakRef() const
{
T* ptr = m_ptr;
m_ptr = 0;
return ptr;
}

       从PassRefPtr类的构造函数可以知道,当它们的参数是T、T&和const RefPtr&时,PassRefPtr类描述的智能指针与RefPtr类描述的智能指针并没有什么区别。*但是当参数为const PassRefPtr&时,也就是我们是一个PassRefPtr智能指针p1构造另一个PassRefPtr智能指针p2时,或者说将一个PassRefPtr智能指针p1传递给另一个PassRefPtr智能指针p2时,神奇的事情就发生了!智能指针p1就自动失去了对目标对象的引用,并且这时候目标对象的引用计数没有发生变化。这是通过PassRefPtr类的成员函数leakRef实现的。

       有了PassRefPtr类之后,我们再来改写上面举的例子,如下所示:

1
2
3
4
5
6
7
8
9
PassRefPtr<T2> foo(PassRefPtr<T1> p2) {
PassRefPtr<T2> p3 = new T2();

......

return p3;
}

PassRefPtr<T1> p1 = new T1();

       此时执行上述代码对于引用计数会有稍许变化:
       1. T1对象和T2对象在函数foo的调用或者返回前后,它们的引用计数值都始终保持为1。

  1. 只有当智能指针p1和智能指针p4超出其生命周期范围时,T1对象和T2对象的引用计数才会减少为0,从而被释放。

       这样就可以免去智能指针作为参数或者返回值传递目标对象的增加和减少引用计数操作,从而去掉无用功。

       从前面PassRefPtr类的定义可以知道,我们可以将一个RefPtr智能指针传递给一个PassRefPtr智能指针,这时候目标对象的引用计数会增加1。我们还可以另外一种方式将一个RefPtr智能指针传递给一个PassRefPtr智能指针。这种传递方式将会使得目标对象的引用计数不发生变化,并且RefPtr智能指针自动失去对目标对象的引用。这是通过调用RefPtr类的成员函数release实现的,这个函数定义在文件external/chromium_org/third_party/WebKit/Source/wtf/RefPtr.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
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;
};

//......
};

       RefPtr类的成员函数release调用函数adoptRef来构造一个PassRefPtr智能指针, 这个函数定义在文件external/chromium_org/third_party/WebKit/Source/wtf/PassRefPtr.h中:

1
2
3
4
5
6
7
8
9
10
11
namespace WTF {
//......

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

//......
};

       函数adoptRef调用以下的PassRefPtr类的构造函数来构造一个PassRefPtr智能指针,这个函数定义在文件external/chromium_org/third_party/WebKit/Source/wtf/PassRefPtr.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
namespace WTF {
//......

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

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

//......

mutable T* m_ptr;
};

//......
};

       从这里就可以看到,上述的PassRefPtr类的构造函数并没有增加目标对象的引用计数,因此我们就可以在不改变目标对象的引用计数的前提下,将一个RefPtr智能指针传递给一个PassRefPtr智能指针,相当于就是将一个RefPtr智能指针对目标对象的所有权转移给一个PassRefPtr智能指针。

第二类智能指针

       接下来我们再看WebKit的第二类智能指针的实现,它由类OwnPtr实现,这个类定义在文件external/chromium_org/third_party/WebKit/Source/wtf/OwnPtr.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
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

       从OwnPtr类的定义可以看到,OwnPtr智能指针只能通过以下构造函数创建:

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

       也就是说,我们创建了一个T对象后,只能先用来创建一个PassOwnPtr对象,然后再用该PassOwnPtr对象来创建一个OwnPtr智能指针。

       当然,我们也可以使用OwnPtr类的以下构造函数来创建一个没有引用任何目标对象的OwnPtr智能指针:

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

       然后,再通过调用操作符重载函数operator=将一个PassOwnPtr对象赋值给OwnPtr智能指针:

1
2
3
4
5
6
7
8
9
10
11
12
template<typename T> template<typename U> inline OwnPtr<T>& OwnPtr<T>::operator=(const PassOwnPtr<U>& o)
{
//......
//对目标对象的引用从PassOwnPtr智能指针转移到了OwnPtr智能指针
PtrType ptr = m_ptr;
m_ptr = o.leakPtr();
//对象与参数o描述的PassOwnPtr智能指针引用的目标对象是不一样的,
//并且该对象会被释放掉。
ASSERT(!ptr || m_ptr != ptr);
OwnedPtrDeleter<T>::deletePtr(ptr);
return *this;
}

       无论是哪一种方式创建OwnPtr智能指针,都必须先创建一个PassOwnPtr对象。PassOwnPtr类描述的也是一个智能指针,它与OwnPtr的关系有点类似我前面分析的RefPtr与PassRefPtr的关系。也就是说,PassOwnPtr智能指针可以作为函数参数或者函数返回值传递。后面我们再详细分析PassOwnPtr智能指针的创建过程。

       PassOwnPtr类有一个成员函数leakPtr,它的作用类似于前面分析的PassRefPtr类的成员函数leakPtr,都是用来放弃一个PassOwnPtr智能指针对目标对象的引用的。在上面描述的两种OwnPtr智能指针创建方式中,被PassOwnPtr智能指针放弃引用的目标对象将被正在创建的OwnPtr智能指针接管。也就是说,对目标对象的引用从PassOwnPtr智能指针转移到了OwnPtr智能指针。

       不过对于通过操作符重载函数operator=创建的OwnPtr智能指针,如果它之前有引用其它的目标对象,那么就要求该对象与参数o描述的PassOwnPtr智能指针引用的目标对象是不一样的,并且该对象会被释放掉。也就是说,当一个OwnPtr智能指针要引用新的目标对象时,必须先释放之前引用的旧目标对象。

       一个OwnPtr智能指针在超出其生命周期范围之前,如果想要放弃对目标对象的引用,那么可以通过调用OwnPtr类的成员函数release或者leakPtr来实现,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
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智能指针引用的目标对象,不过它会返回该目标对象的地址值给调用者,也就是将一个指向目标对象的裸指针返回给调用者。

Move语意

       我们注意到,OwnPtr类的成员函数leakPtr的返回值类型为OwnPtr::PtrType,它的定义如下所示:

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

        RemoveExtent是一个与C++ 11引入的右值引用(Rvalue Reference)有关一个模板类。右值引用通过T&&符号来表示,它主要是用来实现move语意和完美转发(perfect forwarding)。关于右值引用的概念,由于篇幅关系,这里不进行展开,然后强烈建议大家去看看这两篇文章:从4行代码看右值引用Rvalue Reference Declarator: &&。这里我们只需要知道,ValueType描述的是模板参数T的值类型,而PtrType描述的是模板参数T的裸指针类型,不管我们是使用T、T&还是T&&来推导模板参数。

       关于右值引用的move语意,这里值得再解释一下。简单地说,move语意是为了消除临时对象之间的拷贝操作,例如,对于以下表达式来说:

1
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赋值操作符函数,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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->来直接操作目标对象,例如调用目标对象的成员函数,如下所示:

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

       当一个OwnPtr智能指针超出其生命周期范围内时,它所引用的对象就会被释放掉,如下所示:

1
2
3
4
5
~OwnPtr()
{
OwnedPtrDeleter<T>::deletePtr(m_ptr);
m_ptr = 0;
}

       从而就可以起到自动释放不再需要的对象的作用。

       从这里可以看到,OwnPtr智能指针是通过模板类OwnedPtrDeleter< T >的成员函数deletePtr来释放目标对象的,这个函数定义在文件external/chromium_org/third_party/WebKit/Source/wtf/OwnPtrCommon.h中,它的实现如下所示:

1
2
3
4
5
6
7
8
9
template <typename T>
struct OwnedPtrDeleter {
static void deletePtr(T* ptr)
{

COMPILE_ASSERT(!IsRefCounted<T>::value, UseRefPtrForRefCountedObjects);
COMPILE_ASSERT(sizeof(T) > 0, TypeMustBeComplete);
delete ptr;
}
};

       模板类OwnedPtrDeleter的成员函数deletePtr在编译时会通过宏COMPILE_ASSERT来断言参数ptr描述的对象不是从前面我们分析的RefCountedBase类或者ThreadSafeRefCountedBase继承下来的。这意味着OwnPtr智能指针不能用来引用具有引用计数功能的对象。

       这实际上是定义了这样的一个规则:具有引用计数功能的对象,如果要配合智能指针使用,那么就使用RefPtr或者PassRefPtr智能指针;否则的话,就使用OwnPtr或者我们接下来分析的PassOwnPtr智能指针。这个规则要求我们对程序里面实现的类有一个清晰的设计。

       宏COMPILE_ASSERT实际上是利用C++ 11引入的static_assert特性来实现的。这个static_assert语句不会对编译后得到的二进制代码有任何影响,纯粹是编译期间使用的,用来在编译期间就能捕捉到错误。

       从模板类OwnedPtrDeleter< T >的成员函数deletePtr的实现可以知道,当模板类IsRefCounted的静态成员变量value的值等于true的时候,就说明参数ptr描述的对象是从RefCountedBase类或者ThreadSafeRefCountedBase继承下来的。

       模板类IsRefCounted< T >的静态成员变量value的定义如下所示,这个成员变量定义在文件external/chromium_org/third_party/WebKit/Source/wtf/OwnPtrCommon.h中:

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

       从这里就可以看到,如果模板参数T是从RefCountedBase类或者ThreadSafeRefCountedBase类继承下来的,那么模板类IsRefCounted< T >的静态成员变量value就会等于true。

       判断模板参数T是不是从RefCountedBase类或者ThreadSafeRefCountedBase类继承下来,是通过调用模板类IsSubclass< T >的静态成员变量value来实现的。模板类IsSubclass< T >的静态成员变量value的定义如下所示,这个成员变量定义在文件external/chromium_org/third_party/WebKit/Source/wtf/TypeTraits.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19

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);
};

//......
}

       这里的模板参数U就为RefCountedBase或者ThreadSafeRefCountedBase,判断模板参数T是不是它们的子类用了一个很巧妙方法。

       模板类IsSubclass< T >定义了两个版本的静态成员函数subclassCheck,其中一个的参数为U,另一个的参数为可变参数。如果T是从U继承下来的,T就可以自动转化为U,这意味着调用静态成员函数subclassCheck(t),也就subclassCheck(T),会自动匹配为参数为U的成员函数subclassCheck。参数为U的成员函数的返回值为YesType,这就意味着参数T是参数U的子类。

       注意,sizeof是一个编译器运算符号,当它的参数是一个函数的时候,实际上计算的是该函数的返回值的类型所占据的字节数。由于函数的返回值类型通过声明就可以知道,因此模板类IsSubclass就只是声明了静态成员函数subclassCheck,而没有对应的实现。

       前面提到,为了创建OwnPtr智能指针,我们首先要创建PassOwnPtr智能指针。PassOwnPtr智能指针使得OwnPtr智能指针引用的对象也可以作为函数参数或者函数返回值传递,它的定义如下所示,这个类定义在文件external/chromium_org/third_party/WebKit/Source/wtf/PassOwnPtr.h:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
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);
}

......
};

       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类实现,这个类定义在文件external/chromium_org/third_party/WebKit/Source/wtf/WeakPtr.h中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
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;
};

       从WeakPtr类的构造函数可以知道,创建一个WeakPtr弱智能指针需要一个WeakReference对象。这个WeakReference对象就保存在WeakPtr类的成员变量m_ref中。

       在使用一个WeakPtr弱智能指针,首先要调用它的成员函数get检查它引用的目标对象是否还存在。如果存在,WeakPtr类的成员函数get就会返回一个指向目标对象的指针给调用者。否则的话,WeakPtr类的成员函数get返回一个空指针。

       WeakPtr类的成员函数get又是通过成员变量m_ref描述的一个WeakReference对象的成员函数get检查一个WeakPtr智能指针引用的目标对象是否还存在的。

       WeakReference类的实现如下所示,这个类定义在文件external/chromium_org/third_party/WebKit/Source/wtf/WeakPtr.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
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;
......
};

       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类所示,external/chromium_org/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class HTMLDocumentParser :  public ScriptableDocumentParser, private HTMLScriptRunnerHost {
......

protected:
......

HTMLDocumentParser(HTMLDocument&, bool reportErrors);

......

private:
......

WeakPtrFactory<HTMLDocumentParser> m_weakFactory;

......
};

       在创建一个HTMLDocumentParser对象的时候,它的成员变量m_weakFactory就会被初始化,如下所示,这个函数定义在文件external/chromium_org/third_party/WebKit/Source/core/html/parser/HTMLDocumentParser.cpp中:

1
2
3
4
5
6
7
HTMLDocumentParser::HTMLDocumentParser(HTMLDocument& document, bool reportErrors)
: ......
, m_weakFactory(this)
......
{
......
}

       WeakPtrFactory类的定义如下所示,这个类定义在文件external/chromium_org/third_party/WebKit/Source/wtf/WeakPtr.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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;
};

       回到前面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实现的第一类智能指针和第二类智能指针,以及弱智能指针。

第一类智能指针

       Chromium的第一类智能指针由类scoped_refptr实现。由于它要求被引用对象具有计数功能,因此就提供了一个具有计数功能的基类RefCounted。当一个对象可以被类scoped_refptr描述的对象引用时,它就必须要从基类RefCounted继承下来。这一点与WebKit的第一类智能指针RefPtr是类似的。

Chromium的RefCounted类的实现如下所示,这个类定义在文件external/chromium_org/base/memory/ref_counted.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
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);
}
}

......
};

       Chromium的RefCounted类定义了两个成员函数AddRef和Release,分别用来增加和减少目标对象的1个引用计数,并且都是通过调用父类RefCountedBase的成员函数AddRef和Release来实现的。当减少目标对象的1引用计数之后,如果目标对象的引用计数变为0,那么目标对象就会被delete掉。

       Chromium的RefCountedBase类的定义如下所示,这个类定义在文件external/chromium_org/base/memory/ref_counted.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
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_;

......
};

       Chromium的RefCountedBase类与WebKit的RefCountedBase类也是几乎一样,都是提供了非线程安全版本的计数功能。
       Chromium提供的线程安全版本的具有计数功能的基类是RefCountedThreadSafe,它的实现如下所示,这个类定义在文件external/chromium_org/base/memory/ref_counted.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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; }
......
};

       Chromium的RefCountedThreadSafe类与RefCountedBase类的实现也类似,不过有两点区别:

  1. RefCountedThreadSafe类是从RefCountedThreadSafeBase类继承下来的,后者提供了线程安全版本的计数功能。
  2. RefCountedThreadSafe类可以通过模板参数Traits指定一个类,当它引用的目标对象的引用计数等于0的时候,就会调用该参数Traits指定的类的静态成员函数Destruct来释放目标对象。

       第2点是很有用的。比如我们在一个线程创建一个T对象,这个T对象可能会被其它线程引用。当这个T对象的引用计数是在其它线程减少为0时,我们希望它不要在其它线程释放,而是要在创建线程进行释放,那么就可以通过指定参数Traits来实现。

       如果没有指定模板参数Traits,那么它的默认值就为DefaultRefCountedThreadSafeTraits。这个DefaultRefCountedThreadSafeTraits类的实现如下所示,这个类定义在文件external/chromium_org/base/memory/ref_counted.h中:

1
2
3
4
5
6
7
8
template<typename T>
struct DefaultRefCountedThreadSafeTraits {
static void Destruct(const T* x) {
......
RefCountedThreadSafe<T,
DefaultRefCountedThreadSafeTraits>::DeleteInternal(x);
}
};

       DefaultRefCountedThreadSafeTraits类的静态成员函数Destruct又是通过调用RefCountedThreadSafe类的静态成函数DeleteInternal来释放目标对象的。从RefCountedThreadSafe类的实现可以知道,它的静态成员函数DeleteInternal是直接将目标对象delete掉。

       我们再来看RefCountedThreadSafe类的父类RefCountedThreadSafeBase的实现,如下所示,这个类定义在文件external/chromium_org/base/memory/ref_counted.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class BASE_EXPORT RefCountedThreadSafeBase {
......

protected:
......

void AddRef() const;

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

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

       RefCountedThreadSafeBase类提供了两个成员函数AddRef和Release,用来执行线程安全的引用计数加1和减1操作,它们的实现如下所示,这两个函数定义在文件external/chromium_org/base/memory/ref_counted.cc中:

1
2
3
4
5
6
7
8
9
10
11
12
13
void RefCountedThreadSafeBase::AddRef() const {
......
AtomicRefCountInc(&ref_count_);
}

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

       RefCountedThreadSafeBase类的成员函数AddRef和Release分别通过函数AtomicRefCountInc和AtomicRefCountDec对目标对象的引用计数执行原子性的加1和减1操作,因此它们是线程安全的。

       有了RefCounted和ThreadSafeRefCounted这两个基类为对象提供计数功能之后,我们就继续分析Chromium的第一类智能指针scoped_refptr的实现,如下所示,这个类定义在文件external/chromium_org/base/memory/ref_counted.h中。:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
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_;
};

       Chromium的scoped_refptr类的实现与WebKit的RefPtr类的实现几乎是一样的,并且都是要求目标对象是从RefCounted类或者ThreadSafeRefCounted类继承下来的,因此我们就不再详述。

       为了方便创建scoped_refptr智能指针,Chromium提供了一个帮助函数make_scoped_refptr,这个函数定义在文件external/chromium_org/base/memory/ref_counted.h中,它的实现如下所示:

1
2
3
4
template <typename T>
scoped_refptr<T> make_scoped_refptr(T* t) {
return scoped_refptr<T>(t);
}

       函数make_scoped_refptr调用scoped_refptr类的构造函数直接将参数t封装成一个scoped_refptr智能指针,然后返回给调用者。

第二类智能指针

       接下来我们再看Chromium的第二类智能指针的实现,它由类scoped_ptr实现,这个类定义在文件external/chromium_org/base/memory/scoped_ptr.h中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
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_;

......
};

       Chromium的scoped_ptr类与WebKit的OwnPtr类的实现也是类似的:

  1. 通过宏COMPILE_ASSERT以及IsNotRefCounted类的静态成员变量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. scopedptr类将引用的目标对象间接保存在成员变量impl描述的一个scoped_ptr_impl对象中。

       类scoped_ptr_impl的实现如下所示, 这个类定义在文件external/chromium_org/base/memory/scoped_ptr.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
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_;

......
};

       类scoped_ptrimpl将目标对象保存在成员变量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语意的构造函数。

Move语意

       接下来,我们继续分析scoped_ptr智能指针是如何通过宏MOVE_ONLY_TYPE_FOR_CPP_03来禁止copy语意和实现move语意义的。

       宏MOVE_ONLY_TYPE_FOR_CPP_03的定义如下所示,这个宏定义在文件external/chromium_org/base/move.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
#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:

       因此,scoped_ptr类内部的宏MOVE_ONLY_TYPE_FOR_CPP_03(scoped_ptr, RValue)展开后就得到:

1
2
3
4
5
6
7
8
9
10
11
12
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语意的实现过程,如下所示:

1
2
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智能指针(也就是scopedptr智能指针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所示:

弱智能指针

       WeakPtr类继承于WeakPtrBase类。WeakPtrBase类有一个成员变量ref_,它指向一个WeakReference对象。WeakPtr类实现弱智能指针的功能的关键点就在于这个WeakReference对象,那么这个WeakReference对象是怎么来的呢?

       一个对象如果需要被弱智能指针引用,那么它所属的类就必须要继承于SupportsWeakPtr类。SupportsWeakPtr类又是继承于SupportsWeakPtrBase类的。SupportsWeakPtrBase类有一个静态成员函数StaticAsWeakPtr,用来将一个间接继承于SupportsWeakPtr类的子类对象封装成一个WeakPtr弱智能指针。

       SupportsWeakPtr类有一个成员函数AsWeakPtr,用来将一个SupportsWeakPtr子类对象封装成一个WeakPtr弱智能指针。在封装的过程中,需要用到成员变量weak_referenceowner指向的一个WeakReferenceeOwner对象。

       WeakReferenceeOwner类有一个成员变量flag_,它指向一个Flag对象。这个Flag对象有一个布尔类型的成员变量isvalid。当一个WeakReferenceeOwner对象所属的SupportsWeakPtr对象被销毁时,也就是它的析构函数被调用的时候,这个WeakReferenceeOwner对象的成员函数Invalidate就会被调用。WeakReferenceeOwner类的成员函数Invalidate又会进一步调用成员变量flag_指向的一个Flag对象的成员函数Invalidate,将该Flag对象的成员变量isvalid的值设置为false,表示一个相应的SupportsWeakPtr对象被销毁了。

       当我们调用SupportsWeakPtr类的成员函数AsWeakPtr来获得一个WeakPtr弱智能指针时,SupportsWeakPtr类的成员函数AsWeakPtr先会调用成员变量weak_referenceowner指向的一个WeakReferenceeOwner对象的成员函数GetRef创建一个WeakReference对象,这个WeakReference对象通过成员变量flag引用了上述的WeakReferenceeOwner对象的成员变量flag指向的一个Flag对象。这意味在图1中,WeakReferenceeOwner类和WeakReference对象的成员变量flag_指向的都是同一个Flag对象。

       这相当于就是通过一个共同的Flag对象将一个WeakPtr弱智能指针与它所引用的目标对象关联起来,这样我们需要使用一个WeakPtr弱智能指针所引用的目标对象时,就可以通过上述的共同Flag对象的成员函数IsValid来判断目标对象是否已经被销毁,实际上就是判断该Flag对象的成员变量isvalid是否等于true。

       为了更清楚地表达图1所示的WeakPtr类关系图,接下来我们通过代码来分析该类关系图中涉及到的每一个类。

       我们首先看SupportsWeakPtrBase类的实现,这个类定义在文件external/chromium_org/base/memory/weak_ptr.h中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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_));
}
};

       前面提到,一个类如果要配合WeakPtr弱智能指针使用,那么该类必须要从SupportsWeakPtr继承下来,如下所示:

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

       这时候如果有一个类Derived又继承了Base类,如下所示:

1
class Derived : public Base {};

       这时候如果我们有一个Derived对象,并且想创建一个WeakPtr智能指针引用该Derived对象,那么是不能调用从父类Base继承下来的成员函数AsWeakPtr来创建的,如下所示:

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

       这是因为SupportsWeakPtr< Base >类的成员函数AsWeakPtr返回的是一个WeakPtr< Base >对象,并且这个WeakPtr< Base >对象不能自动转换为一个WeakPtr< Derived >对象。‘

       为了能够实现上述转换,我们要使用到一个AsWeakPtr函数,这个函数定义在文件external/chromium_org/base/memory/weak_ptr.h中,它的实现如下所示:

1
2
3
4
5

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

       有了这个AsWeakPtr函数之后,就可以通过下面的代码来创建一个WeakPtr< Derived >对象:

1
2
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对象所引用的目标对象的地址值。有了目标对象的地址值之后,就可以将它转化一个Derived指针。这里能够转换成功,是因为Derived类是继承于Base类的。有了这个Derived指针之后,再结合前面获得的WeakPtr对象的成员变量ref_r描述的一个WeakReference对象之后,就可以通过模板类WeakPtr< Derived >的构造函数来创建一个WeakPtr< Derived >对象了,也就是创建一个引用了Derived对象的WeakPtr智能指针了。后面我们分析WeakPtr类的实现时,就会更清楚地理解上述过程。

       接着我们看SupportsWeakPtr类的实现,这个类定义在文件external/chromium_org/base/memory/weak_ptr.h中,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

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_;
......
};

       SupportsWeakPtr类最主要的就是通过成员变量weak_referenceowner引用了一个WeakReferenceOwner对象。

       WeakReferenceOwner类的定义如下所示,这个类定义在文件external/chromium_org/base/memory/weak_ptr.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class BASE_EXPORT WeakReferenceOwner {
public:
......
~WeakReferenceOwner();

WeakReference GetRef() const;

......

void Invalidate();

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

       WeakReferenceOwner类最主要的就是通过成员变量flag_引用了一个Flag对象。注意,这个成员变量通过scoped_refptr智能指针来引用Flag对象。这意味着Flag类是具有引用计数功能的。

       Flag类的定义如下所示,这个类定义在文件external/chromium_org/base/memory/weak_ptr.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
class BASE_EXPORT WeakReference {
public:
......
class BASE_EXPORT Flag : public RefCountedThreadSafe<Flag> {
public:
Flag();

void Invalidate();
bool IsValid() const;

private:
......

bool is_valid_;
};

......
};

       Flag类继承于RefCountedThreadSafe类,因此它就具有引用计数功能,并且在增加和减少引用计数时是线程安全的。

       现在我们分析前面列出的SupportsWeakPtr类的成员函数AsWeakPtr的实现,它首先是调用成员变量weak_referenceowner指向的一个WeakReferenceOwner对象的成员函数GetRef获得一个WeakReference对象,然后再通过WeakPtr类的构造函数创建一个WeakPtr弱智能指针。

       WeakReferenceOwner类的成员函数GetRef的实现如下所示,这个函数定义在文件external/chromium_org/base/memory/weak_ptr.cc中:

1
2
3
4
5
6
7
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());
}

       WeakReferenceOwner类的成员函数GetRef首先是调用成员函数HasRefs判断成员变量flag是否指向了一个Flag对象。如果不是,那么就需要创建一个Flag对象,并且保存在成员变量flag中。

       WeakReferenceOwner类的成员函数GetRef接下来再以成员变量flag_指向的Flag对象为参数,创建一个WeakReference对象。

       WeakReference类的定义如下所示,这个类定义在文件external/chromium_org/base/memory/weak_ptr.h中:

1
2
3
4
5
6
7
8
9
10
11
12

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

explicit WeakReference(const Flag* flag);

......

private:
scoped_refptr<const Flag> flag_;
};

       WeakReference类以const Flag*为参数的构造函数的实现如下所示,这个函数定义在文件external/chromium_org/base/memory/weak_ptr.cc中:

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

       它主要就是用参数flag描述的一个Flag对象来初始化成员变量flag_描述的一个scoped_refptr智能指针。

       回到SupportsWeakPtr类的成员函数AsWeakPtr中,得到了一个WeakReference对象之后,就可以创建一个WeakPtr对象了。

       WeakPtr类的定义如下所示,这个类定义在文件external/chromium_org/base/memory/weak_ptr.h中:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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_;
};

       从这里就可以看到,WeakPtr类是从WeakPtrBase类继承下来的,后者的定义如下所示,这个类定义在文件external/chromium_org/base/memory/weak_ptr.h中:

1
2
3
4
5
6
7
8
9
class BASE_EXPORT WeakPtrBase {
public:
......

protected:
explicit WeakPtrBase(const WeakReference& ref);

WeakReference ref_;
};

       WeakPtrBase类主要就是通过成员变量ref_引用一个WeakReference对象。

       当我们调用WeakPtr类的构造函数将一个WeakReference对象和一个目标对象封装成一个WeakPtr对象的时候,WeakPtr类的构造函数会调用父类WeakPtrBase的构造函数将上述WeakReference对象保存在其成员变量ref_中,如下所示,这个函数定义在文件external/chromium_org/base/memory/weak_ptr.cc中:

1
2
3

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

       再回到WeakPtr类以WeakReference对象和T对象指针为参数的构造函数中,它将WeakReference对象保存在父类WeakPtrBase的成员变量ref之后,还会将T对象指针保存在自己的成员变量ptr中,该成员变量描述的就是一个WeakPtr弱智能指针所引用的目标对象。

       这里有一点需要注意的是,WeakPtr类的成员变量ptr_指向的T对象有可能是已经被销毁了的,这是因为弱智能指针不能阻止它所引用的对象被销毁。也就是说,当我们在使用一个WeakPtr弱智能指针的时候,它所引用的目标对象有可能是已经被销毁的。我们可以通过WeakPtr类的成员函数get来判断一个WeakPtr弱智能指针所引用的目标对象是否已经被销毁。

       WeakPtr类的成员函数get调用从父类WeakPtrBase继承下来的成员变量ref_指向的一个WeakReference对象的成员函数isvalid判断一个WeakPtr弱智能指针所引用的目标对象是否已经被销毁。如果还没有被销毁,那么就说明WeakPtr类的成员变量ptr描述的对象地址是有效的,因此就将它返回调用者。如果已经被销毁,那么就返回一个NULL值给调用者。

       WeakReference类的成员函数is_valid的实现如下所示,这个函数定义在文件external/chromium_org/base/memory/weak_ptr.cc中:

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

       WeakReference类的成员函数isvalid首先判断成员变量flag是否指向了一个Flag对象。如果指向了一个Flag对象,那么就调用它的成员函数IsValid判断它所关联的一个对象是否还存活。

       Flag类的成员函数IsValid的实现如下所示,这个函数定义在文件external/chromium_org/base/memory/weak_ptr.cc中:

1
2
3
4
bool WeakReference::Flag::IsValid() const {
......
return is_valid_;
}

       Flag类的成员函数IsValid直接将成员变量isvalid的值返回给调用者。

       从前面分析的WeakReferenceOwner类的成员函数GetRef可以知道,上述的Flag对象即为目标对象通过WeakReferenceOwner类的关联的那个Flag对象,这个Flag对象在创建的时候,它的成员变量isvalid被设置为true,如下所示,这个函数定义在文件external/chromium_org/base/memory/weak_ptr.cc中:

1
2
3
WeakReference::Flag::Flag() : is_valid_(true) {
......
}

       当一个Flag对象的宿主WeakReferenceOwner对象被析构时,就会将它的成员变量isvalid的值设置为false,如下所示,这个函数定义在文件external/chromium_org/base/memory/weak_ptr.cc中:

1
2
3
WeakReferenceOwner::~WeakReferenceOwner() {
Invalidate();
}

       WeakReferenceOwner类的析构函数调用了另外一个成员函数Invalidate,它的实现如下所示,这个函数定义在文件external/chromium_org/base/memory/weak_ptr.cc中:

1
2
3
4
5
6
void WeakReferenceOwner::Invalidate() {
if (flag_.get()) {
flag_->Invalidate();
flag_ = NULL;
}
}

       WeakReferenceOwner类的成员函数Invalidate首先检查成员变量flag_是否指向了一个Flag对象。如果指向了,那么就调用该Flag对象的成员函数Invalidate将它的成员变量isvalid的值设置为false,如下所示,这个函数定义在文件external/chromium_org/base/memory/weak_ptr.cc中:

1
2
3
4
void WeakReference::Flag::Invalidate() {
......
is_valid_ = false;
}

       那么,一个WeakReferenceOwner对象什么时候会被析构呢?前面提到,SupportsWeakPtr类有一个成员变量weak_referenceowner描述的是一个WeakReferenceOwner对象。这意味着当一个SupportsWeakPtr对象被析构时,它的成员变量weak_referenceowner描述的是一个WeakReferenceOwner对象也会随之析构。

       SupportsWeakPtr类是作为一个被WeakPtr弱智能指针引用的目标对象的父类的,因此当一个WeakPtr弱智能指针引用的目标对象被析构时,目标对象的父类SupportsWeakPtr有的成员变量weak_referenceowner描述的是一个WeakReferenceOwner对象就会被析构,随后又会将与该WeakReferenceOwner对象关联的Flag对象的成员变量isvalid的值设置为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的智能指针设计相对来说就比较粗略,没有太多可选择的余地。

妹子图

坚持技术分享,您的支持将鼓励我继续创作!