普通指针的使用隐患和智能指针的解决办法
普通指针在使用过程中的三个隐患:
- 指针没有初始化。
- 指针没有释放。
- 释放后没有置空。
智能指针的解决办法:
- 构造函数中初始化。
- 使用计数器,计数器为0时释放。
- 将指针封装在智能指针中,析构时释放。
标准库中的智能指针为shared_ptr,而android Binder相关代码使用wp,sp作为智能指针。
android中的智能指针
计数器由object自身持有
计数器并非由智能指针拥有(这是我之前一直弄错的地方)。如果计数器由智能指针拥有,那么当有多个智能指针都由该object初始化的时候,它们的计数值不能共享,也就无法确定该object究竟被引用了多少次。
解决办法就是让计数器由object自身持有,这样只要在初始化智能指针的时候更新object持有的计数器,计数值就自然而然地被所有智能指针共享了。每个智能指针都可以从object的计数器中知道该object还剩多少个引用,从而决定是否释放。
android中定义了统一的父类“RefBase”和“LightRefBase”,所有有计数要求的类型都继承于这两个类。
这是sp和wp的实现方法,暂不清楚stl中是怎么实现智能指针的。
另外,sp中重载了->操作符,sp指针->的效果等价于原指针->的效果
弱指针wp的构造
弱指针是为了避免智能指针引用中的“死锁”而产生的,“死锁”现象即两个类的成员变量中均有指向对方的智能指针,这样就会导致这两个类的智能指针引用计数永远都不为零。
弱引用有两个特点:
- 当强引用为0时,无论弱引用是多少,对象都会被delete(android中有例外?)。
- 弱指针必须先升级成强指针,才能访问它所指的对象。
wp的参数为指针的构造函数如下:
//frameworks\rs\cpp\util\RefBase.h
template<typename T>
wp<T>::wp(T* other)
: m_ptr(other)
{
if (other) m_refs = other->createWeak(this);
}
构造函数把指针赋给自己的成员m_ptr,然后以自身作为参数调用了createWeak函数,createWeak函数属于前面提到的父类RefBase:
//android-7.1.1_r1\system\core\libutils\RefBase.cpp
RefBase::weakref_type* RefBase::createWeak(const void* id) const
{
mRefs->incWeak(id);
return mRefs;
}
createWeak将wp指针作为id参数调用了mRefs的incWeak函数(从之后的代码来看,id仅是用来调试用的,这里可以忽略),并将mRef返回。mRef将被赋值给wp的m_refs。
mRef的类型是weakref_impl,而wp的m_refs成员的类型是weakref_type,weakref_impl继承weakref_type,从名字就可以看到前者是后者的实现类,可以把这两者当作相同类型看待。
weakref_impl非常重要,截取weakref_impl的定义如下:
class RefBase::weakref_impl : public RefBase::weakref_type
{
public:
std::atomic<int32_t> mStrong;
std::atomic<int32_t> mWeak;
RefBase* const mBase;
std::atomic<int32_t> mFlags;
#if !DEBUG_REFS
weakref_impl(RefBase* base)
: mStrong(INITIAL_STRONG_VALUE)
, mWeak(0)
, mBase(base)
, mFlags(0)
{
}
...
}
其中的mStrong和mWeak就是强弱引用计数,mBase是weakref_impl的从属类RefBase的指针,之所以这样设计是为了便于释放RefBase。从这里可以看出weakref_impl即是统一父类RefBase的计数器。接下来看incWeak函数:
void RefBase::weakref_type::incWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->addWeakRef(id);
const int32_t c __unused = impl->mWeak.fetch_add(1,
std::memory_order_relaxed);
ALOG_ASSERT(c >= 0, "incWeak called on %p after last weak ref", this);
}
第一行代码用于强制类型转换,将weakref_type强制转化成weakref_impl,第二行是debug代码,release版本中addWeakRef并没有实现。第三行即是将weakref_impl的mWeak+1,这里实现了计数器的自增。
这里的写法非常奇特,是我以前没见过的写法,incWeak属于weakref_type,其内部实现却调用了子类weakref_Impl的函数,不知道为什么要这样设计。(有点好奇这样的写法是怎么通过编译的)
各个类的关系如下:
强指针sp的构造
强指针的代码比弱指针的代码简单,它的构造函数如下
//\frameworks\rs\cpp\util\StrongPointer.h
template<typename T>
sp<T>::sp(T* other)
: m_ptr(other)
{
if (other) other->incStrong(this);
}
直接调用了”统一父类“RefBase的incStrong函数。这里注意和弱指针的不同,弱指针的incWeak在weakref_type类中,而强指针的incStrong在RefBase类中。继续看incStrong:
void RefBase::incStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->incWeak(id);
refs->addStrongRef(id);
const int32_t c = refs->mStrong.fetch_add(1, std::memory_order_relaxed);
ALOG_ASSERT(c > 0, "incStrong() called on %p after last strong ref", refs);
#if PRINT_REFS
ALOGD("incStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
if (c != INITIAL_STRONG_VALUE) {
return;
}
int32_t old = refs->mStrong.fetch_sub(INITIAL_STRONG_VALUE,
std::memory_order_relaxed);
// A decStrong() must still happen after us.
ALOG_ASSERT(old > INITIAL_STRONG_VALUE, "0x%x too small", old);
refs->mBase->onFirstRef();
}
就是强弱引用计数自增。后面的代码是处理第一次引用的情况的,可以忽略。
强弱指针的删除
引用的自减由decStrong和decWeak完成,和前面一样,decStrong属于RefBase,而decWeak属于weakref_type。
强指针的析构会调用decStrong先使强引用减一,再调用decWeak使弱引用减一(注意先后顺序)。弱指针的析构会调用decWeak使弱引用减一。当强弱引用为0时,就要判断是否要释放指针和计数器,即RefBase和weakref_impl。考虑以下几种情况:
- 强引用为0,弱引用不为0。强指针的析构可能会导致该情况的发生。此时decStrong会释放RefBase,但decWeak不会释放weakref_impl,即wp的m_ref成员依然有效。
- 弱引用为0,强引用为0,且指针曾被强引用过。这种情况只可能发生在情况1之后,此时RefBase已被释放,decWeak只需释放weakref_impl即可。
- 弱引用为0,强引用为0,且指针未被强引用过。这种情况下,由于没有强引用,也就没有强指针的析构,decStrong不会被调用,故RefBase释放的任务由decWeak完成。而RefBase的析构函数中,当弱引用为0时,会释放weakref_impl,这就完成了指针的计数器的释放。
虽然weakref_impl是由RefBase创建的,但在情况1和情况2中,它却是由自己完成自己的释放(在weakref_type的incWeak中完成释放,难道这就是分weakref_impl和weakref_type的理由?),只有在情况3中它才被RefBase释放。这是由于强引用为0时,弱引用不一定为0,所以此时计数器不能随着RefBase释放。只有当强弱引用都为0时才能释放计数器。
理解了以上概念后,再看代码就不难了:
void RefBase::decStrong(const void* id) const
{
weakref_impl* const refs = mRefs;
refs->removeStrongRef(id);
const int32_t c = refs->mStrong.fetch_sub(1, std::memory_order_release);
#if PRINT_REFS
ALOGD("decStrong of %p from %p: cnt=%d\n", this, id, c);
#endif
ALOG_ASSERT(c >= 1, "decStrong() called on %p too many times", refs);
if (c == 1) {
std::atomic_thread_fence(std::memory_order_acquire);
refs->mBase->onLastStrongRef(id);
int32_t flags = refs->mFlags.load(std::memory_order_relaxed);
if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
delete this;
// Since mStrong had been incremented, the destructor did not
// delete refs.
}
}
// Note that even with only strong reference operations, the thread
// deallocating this may not be the same as the thread deallocating refs.
// That's OK: all accesses to this happen before its deletion here,
// and all accesses to refs happen before its deletion in the final decWeak.
// The destructor can safely access mRefs because either it's deleting
// mRefs itself, or it's running entirely before the final mWeak decrement.
refs->decWeak(id);
}
void RefBase::weakref_type::decWeak(const void* id)
{
weakref_impl* const impl = static_cast<weakref_impl*>(this);
impl->removeWeakRef(id);
const int32_t c = impl->mWeak.fetch_sub(1, std::memory_order_release);
ALOG_ASSERT(c >= 1, "decWeak called on %p too many times", this);
if (c != 1) return;
atomic_thread_fence(std::memory_order_acquire);
int32_t flags = impl->mFlags.load(std::memory_order_relaxed);
if ((flags&OBJECT_LIFETIME_MASK) == OBJECT_LIFETIME_STRONG) {
// This is the regular lifetime case. The object is destroyed
// when the last strong reference goes away. Since weakref_impl
// outlive the object, it is not destroyed in the dtor, and
// we'll have to do it here.
if (impl->mStrong.load(std::memory_order_relaxed)
== INITIAL_STRONG_VALUE) {
// Special case: we never had a strong reference, so we need to
// destroy the object now.
delete impl->mBase;
} else {
// ALOGV("Freeing refs %p of old RefBase %p\n", this, impl->mBase);
delete impl;
}
} else {
// This is the OBJECT_LIFETIME_WEAK case. The last weak-reference
// is gone, we can destroy the object.
impl->mBase->onLastWeakRef(id);
delete impl->mBase;
}
}
参考资料:深入理解Android内核设计思想