相信很多朋友都知道,弱引用是Juce源码中的一个亮点。那么什么是弱引用,又有什么用途呢?
假如我写了这样一个程序,A *a = new A(); A *b = a; 这时,a与b指向同一个A的对象,当我在一个地方执行delete a;在另一个地方再使用b的话,肯定会出问题,因为b指向的对象已经被释放,如何避免这种问题呢,有的朋友应该会想到,加一个判断 if(b){ b->test();}这样或许可以,但是实际上这样并不好使。因为不管是a还是b都是一个指针,存储的都是一个4字节的地址,delete a以后,并不影响b的值,也就是说b存储的地址并没有发生改变,只是b所指向的内存已经不见了。
弱引用的出现就是解决此类问题的,如果使用弱引用的话,就可以这样写A* a = new A(); WeakReference<A> wa = a; A* p1 = wa; 这时delete p1后,在调用A* p2 = wa;时就会发现p2为NULL。这是怎么做到的呢?下面我们来分析Juce的源码
在分析弱引用之前,先要分析ReferenceCountedObject和ReferenceCountedObjectPtr,即引用计数对象和引用计数对象指针。因为弱引用的引用生命周期还是要靠这两个类来维护的,这里要注意一点,弱引用的引用对象不是实际对象,计数器无法管理实际对象的生命周期,管理的是引用对象,也就是说他所管理的是指向对象的指针而不是对象本身。所以说ReferenceCountedObjectPtr可以理解为指针的智能指针。
下面开始上源码
class JUCE_API ReferenceCountedObject //引用计数对象
{
public:
//==============================================================================
/** Increments the object's reference count.
This is done automatically by the smart pointer, but is public just
in case it's needed for nefarious purposes.
计数自增
*/
void incReferenceCount() noexcept
{
++refCount;
}
/** Decreases the object's reference count.
If the count gets to zero, the object will be deleted.
计数自减,当减到0时释放对象
*/
void decReferenceCount() noexcept
{
jassert (getReferenceCount() > 0);
if (--refCount == 0)
delete this;
}
/** Decreases the object's reference count.
If the count gets to zero, the object will not be deleted, but this method
will return true, allowing the caller to take care of deletion.
计数自减,但不释放对象
*/
bool decReferenceCountWithoutDeleting() noexcept
{
jassert (getReferenceCount() > 0);
return --refCount == 0;
}
/** Returns the object's current reference count.
获取计数
*/
int getReferenceCount() const noexcept { return refCount.get(); }
protected:
//==============================================================================
/** Creates the reference-counted object (with an initial ref count of zero). */
ReferenceCountedObject() {}
/** Destructor. */
virtual ~ReferenceCountedObject()
{
// it's dangerous to delete an object that's still referenced by something else!
jassert (getReferenceCount() == 0);
}
/** Resets the reference count to zero without deleting the object.
You should probably never need to use this!
*/
void resetReferenceCount() noexcept //清零
{
refCount = 0;
}
private:
//==============================================================================
Atomic <int> refCount;
friend struct ContainerDeletePolicy<ReferenceCountedObject>;
JUCE_DECLARE_NON_COPYABLE (ReferenceCountedObject)
};
这个应该非常好理解,就是一个类,里面维护一个原子int型变量,可以使用对象调用incReferenceCount和decReferenceCount两个方法让该变量自增或自减,当计数递减到0时对象析构,稍微想想也知道,这个东西本身没什么用,而是一个配角。
下面我们来看一下这个指针的智能指针
template <class ReferenceCountedObjectClass>
class ReferenceCountedObjectPtr
{
public:
/** The class being referenced by this pointer. */
typedef ReferenceCountedObjectClass ReferencedType;
//==============================================================================
/** Creates a pointer to a null object. */
ReferenceCountedObjectPtr() noexcept
: referencedObject (nullptr)
{
}
/** Creates a pointer to an object.
This will increment the object's reference-count.
创建对象时,计数对象的计数器自增
*/
ReferenceCountedObjectPtr (ReferencedType* refCountedObject) noexcept
: referencedObject (refCountedObject)
{
incIfNotNull (refCountedObject);
}
/** Copies another pointer.
This will increment the object's reference-count.
拷贝对象时,计数对象的计数器要自增
*/
ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr& other) noexcept
: referencedObject (other.referencedObject)
{
incIfNotNull (referencedObject);
}
/** Copies another pointer.
This will increment the object's reference-count (if it is non-null).
拷贝其他对象的指针对象,计数器自增
*/
template <class Convertible>
ReferenceCountedObjectPtr (const ReferenceCountedObjectPtr<Convertible>& other) noexcept
: referencedObject (static_cast <ReferencedType*> (other.get()))
{
incIfNotNull (referencedObject);
}
/** Changes this pointer to point at a different object.
The reference count of the old object is decremented, and it might be
deleted if it hits zero. The new object's count is incremented.
赋值
*/
ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr& other)
{
return operator= (other.referencedObject);
}
/** Changes this pointer to point at a different object.
The reference count of the old object is decremented, and it might be
deleted if it hits zero. The new object's count is incremented.
*/
template <class Convertible>
ReferenceCountedObjectPtr& operator= (const ReferenceCountedObjectPtr<Convertible>& other)
{
return operator= (static_cast<ReferencedType*> (other.get()));
}
/** Changes this pointer to point at a different object.
The reference count of the old object is decremented, and it might be
deleted if it hits zero. The new object's count is incremented.
赋值时,源对象的计数要自增,被替换对象的计数要自减
*/
ReferenceCountedObjectPtr& operator= (ReferencedType* const newObject)
{
if (referencedObject != newObject)
{
incIfNotNull (newObject);
ReferencedType* const oldObject = referencedObject;
referencedObject = newObject;
decIfNotNull (oldObject);
}
return *this;
}
#if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
/** Takes-over the object from another pointer. */
ReferenceCountedObjectPtr (ReferenceCountedObjectPtr&& other) noexcept
: referencedObject (other.referencedObject)
{
other.referencedObject = nullptr;
}
/** Takes-over the object from another pointer. */
ReferenceCountedObjectPtr& operator= (ReferenceCountedObjectPtr&& other)
{
std::swap (referencedObject, other.referencedObject);
return *this;
}
#endif
/** Destructor.
This will decrement the object's reference-count, which will cause the
object to be deleted when the ref-count hits zero.
对象析构,计数要自减
*/
~ReferenceCountedObjectPtr()
{
decIfNotNull (referencedObject);
}
//==============================================================================
/** Returns the object that this pointer references.
The pointer returned may be null, of course.
*/
operator ReferencedType*() const noexcept { return referencedObject; }
/** Returns the object that this pointer references.
The pointer returned may be null, of course.
*/
ReferencedType* get() const noexcept { return referencedObject; }
/** Returns the object that this pointer references.
The pointer returned may be null, of course.
*/
ReferencedType* getObject() const noexcept { return referencedObject; }
// the -> operator is called on the referenced object
ReferencedType* operator->() const noexcept
{
jassert (referencedObject != nullptr); // null pointer method call!
return referencedObject;
}
private:
//==============================================================================
ReferencedType* referencedObject;
static void incIfNotNull (ReferencedType* o) noexcept
{
if (o != nullptr)
o->incReferenceCount();
}
static void decIfNotNull (ReferencedType* o) noexcept
{
if (o != nullptr && o->decReferenceCountWithoutDeleting())
ContainerDeletePolicy<ReferencedType>::destroy (o);
}
};
这个稍微有点难以理解了, 这个智能指针所维护的就是上面所提到的带有计数器的对象,给每一个所要维护的对象都进行计数,对于同一个计数器对象,当增加一个ReferenceCountedObjectPtr对象的指向时,对象的计数器要自加,当解除一个ReferenceCountedObjectPtr对象的指向时,对象计数器要自减,引起对象计数器自增或自减的操作主要有ReferenceCountedObjectPtr对象的创建、拷贝、析构、赋值传递,当对象计数器自减到0时,对象本身就会析构。
最后再提一点,ReferenceCountedObjectPtr所维护的ReferenceCountedObject对象到底是用来干什么的呢?在Juce的弱引用里面,ReferenceCountedObject本身就是指向对象的一个简单的智能指针,而ReferenceCountedObjectPtr又是管理ReferenceCountedObject对象的带有引用计数的复杂智能指针,所以我给ReferenceCountedObjectPtr起了一个非常形象的名字 —— 指针的智能指针,相信大家能理解。