写在前面
用自己的话分析清楚~
智能指针是如何使用的?
强指针是如何实现?
弱指针如何转化为强指针?
智能指针的使用
智能指针的使用必须满足如下条件:
这个类需要继承自RefBase
为什么需要虚析构函数?
虚析构函数是为了解决这样的一个问题:的指向派生类对象,并用基类的指针删除派生类对象。虚函数的出现是为了解决多态问题。
满足上述条件的类就可以定义智能指针了,普通的指针使用如下方式:
MyClass *p_obj;
智能指针是这样定义:
Sp<MyClass> p_obj;
强指针的使用:
MyClass *p_obj;
1 p_obj = new MyClass(); // 注意不要写成 p_obj = new sp<MyClass>
2 sp<MyClass> p_obj2 = p_obj;
3 p_obj->func();
4 p_obj = create_obj();
5 some_func(p_obj);
弱指针的使用:
1 wp<MyClass> wp_obj = new MyClass();
2 p_obj = wp_obj.promote(); // 升级为强指针。不过这里要用.而不是->,真是有负其指针之名啊
3 wp_obj = NULL;
与普通指针相比,智能指针的特点
- 智能指针解决了对象自动释放的问题
- 智能指针其实更像引用
- 智能指针与普通指针相比,消耗更多的资源。
设计原理
智能指针是通过强弱引用计数来维护一个对象的生命周期的,如果强引用计数小于零的时候,会自动释放空间资源。
我们结合内部的实现,分析一下这段代码的执行:
- MyClass *p_obj;
- p_obj = new MyClass(); // 注意不要写成 p_obj = new sp<MyClass>
- sp<MyClass> p_obj2 = p_obj;
- p_obj->func();
- p_obj = create_obj();
- some_func(p_obj);
我们按照程序的执行流程来分析下内部代码的实现
源码位于:
MyClass *p_obj;
p_obj = new MyClass(); // 注意不要写成 p_obj = new sp<MyClass>
MyClass继承自RefBase,所以:
::()
: (new (this))
{
}
初始化了成员变量
(* )
: ()
, (0)
, ()
, (0)
{
}
public:
volatile ; //强引用计数
volatile ; //弱引用计数
* const ;
volatile ;
其中mFlags 用来描述对象的生命周期控制方式。取值可以使
//! Flags for extendObjectLifetime()
enum {
= 0x0000, //只与强引用计数有关
= 0x0001,
= 0x0001
};
(http://androidxref.com/4.4.3_r1.1/xref/system/core/include/utils/RefBase.h)
这里初始化了目标对象,对象内部初始化了强弱引用计数,及控制对象生命周期模式。
- sp<MyClass> p_obj2 = p_obj;
结合第一部分对sp实现的描述,
< T>
<T>::(T* )
: () {
if ()
->();
}
使用传递来的形参初始化m_ptr, 这其实是对目标对象多了一次引用,所以这里调用()来增加一个强引用计数。
void ::(const void* ) const
{
* const = ;
->();
->();
const c = (&->);
(c > 0, "incStrong() called on %p after last strong ref", );
#if
("incStrong of %p from %p: cnt=%d\n", this, , c);
#endif
if (c != ) {
return;
}
//如果等于初值,说明是第一次,则先减去初值。调用函数()
(-, &->);
->->();
}
In RefBase.cpp 实现,用户的程序中,如有需要可重载之。
void ::()
{
}
void ::::(const void* )
{
* const = static_cast<*>(this);
->();
const c = (&->);
(c >= 0, "incWeak called on %p after last weak ref", this);
}
#if !
(* )
: ()
, (0)
, ()
, (0)
{
}
void (const void* /*id*/) { }
void (const void* /*id*/) { }
void (const void* /*old_id*/, const void* /*new_id*/) { }
void (const void* /*id*/) { }
void (const void* /*id*/) { }
void (const void* /*old_id*/, const void* /*new_id*/) { }
void () const { }
void (bool, bool) { }
#else
我们看到在release版本中(const void* /*id*/)等为空函数。
由调用系统函数() 实现对&->增加。
最终的计数单元其实是在对象中的* ;中的mWeak,mStrong.
接下来我们看随着sp指针作用域的结束,其调用自身的析构函数对对象内的计数自减操作。
下面看sp的析构函数的定义
< T>
<T>::~() {
if ()
->();
}
void ::(const void* ) const
{
* const = ;
->();
const c = (&->);
#if
("decStrong of %p from %p: cnt=%d\n", this, , c);
#endif
(c >= 1, "decStrong() called on %p too many times", );
if (c == 1) {
//这里说明引用的次数已经为0,()函数返回的是执行之前的值。
//调用对象的结束前的函数();
//释放对象
->->();
if ((->&) == ) {
delete this;
}
}
->();
}
整体分析下来,感觉智能指针的整个代码还是比较简单和清晰的。强弱引用计数具体的计数操作是在每个对象中的成员变量:类型的mrefs实现的。 类型继承自::,::其中定义了具体技术的实现,使用变量存储强弱引用计数,使用android系统提供的原子操作对这些变量进行加减。提供封装出变量的增加,减少函数供智能指针中的引用计数函数调用。 智能指针利用封装的模板类的构造函数和析构函数自动的调用计数函数实现对对象的调用管理,当引用次数为0时,释放对象空间。
RefBase同时定义了函数
void ();
void (const void* );
bool ( , const void* );
void (const void* );
分别定义了第一次引用对象的时候执行的函数();
最后一次强引用对象时的函数(const void* );
最后一次弱引用对象时的函数(const void* );
在看看 wp弱指针
// 这个函数用于将wp指针升级为sp指针
//其主要判断所指向的对象是否已经释放以及是否可以增加强指针计数
//如果ok,则返回强指针
< T>
<T> <T>::() const
{
<T> ;
if ( && ->(&)) {
.();
}
return ;
}
bool ::::(const void* )
{
();
* const = static_cast<*>(this);
= ->;
( >= 0,
"attemptIncStrong called on %p after underflow", this);
while ( > 0 && != ) {
// we're in the / case of promoting a weak-reference
// from an existing strong reference.
if ((, +1, &->) == 0) {
break;
}
// the strong count has changed on us, we need to re-assert our
// situation.
= ->;
}
一个弱指针所引用的对象,可能处于两种情况。第一种情况该对象同时也被其他对象引用(此时其值应大于0,且不等于初值)对于这种情况比较好处理。
因为同一个对象可能有多个对象在引用,所以这里加了这么多判断主要是为了 数据的同步。
if ( <= 0 || == ) {
// we're now in the harder case of either:
// - there never was a strong reference on us
// - or, all strong references have been released
if ((->&) == ) {
// this object has a "normal" life-time, i.e.: it gets destroyed
// when the last strong reference goes away
if ( <= 0) {
// the last strong-reference got released, the object cannot
// be revived.
();
return false;
}
// here, curCount == INITIAL_STRONG_VALUE, which means
// there never was a strong-reference, so we can try to
// promote this object; we need to do that atomically.
while ( > 0) {
if ((, + 1,
&->) == 0) {
break;
}
// the strong count has changed on us, we need to re-assert our
// situation (e.g.: another thread has /'ed us)
= ->;
}
if ( <= 0) {
// promote() failed, some other thread destroyed us in the
// meantime (i.e.: strong count reached zero).
();
return false;
}
} else {
// this object has an "extended" life-time, i.e.: it can be
// revived from a weak-reference only.
// Ask the object's implementation if it agrees to be revived
if (!->->(, )) {
// it didn't so give-up.
();
return false;
}
// grab a strong-reference, which is always safe due to the
// extended life-time.
= (&->);
}
// If the strong reference count has already been incremented by
// someone else, the implementor of onIncStrongAttempted() is holding
// an unneeded reference. So call onLastStrongRef() here to remove it.
// (No, this is not pretty.) Note that we MUST NOT do this if we
// are in fact acquiring the first reference.
if ( > 0 && < ) {
->->();
}
}
->();
#if
("attemptIncStrong of %p from %p: cnt=%d\n", this, , );
#endif
// now we need to fix-up the count if it was INITIAL_STRONG_VALUE
// this must be done safely, i.e.: handle the case where several threads
// were here in attemptIncStrong().
= ->;
while ( >= ) {
( > ,
"attemptIncStrong in %p underflowed to INITIAL_STRONG_VALUE",
this);
if ((, -,
&->) == 0) {
break;
}
// the strong-count changed on us, we need to re-assert the situation,
// for e.g.: it's possible the fix-up happened in another thread.
= ->;
}
return true;
}
这里结合代码分析下,弱指针升级为强指针的过程。
这块随后补上~
QQ群 计算机科学与艺术 272583193
加群链接: