Juce源码分析(二)内存的智能管理

         Juce虽然有很多优点,值得我们去学习,但是它的代码十分复杂,所以分析起来相当有难度。所以Juce的源码分析是一个相当漫长的过程,为了减小难度,笔者采用自底向上的分析方式。也就是说先去分析它的底层代码,再去分析应用层。


这里我们看到juce_core模块算是Juce库的核心模块了,所以我们先来分析juce_core这个模块。其中memory部分还是比较独立的,也是比较重要的,所以我们先来分析memory里面的代码。

juce_Memory.h

/** Fills a block of memory with zeros. */
/**给指定大小和位置的内存填充零 */
inline void zeromem (void* memory, size_t numBytes) noexcept        
{
	memset (memory, 0, numBytes);
}

/** Overwrites a structure or object with zeros. */
/** 将结构体的所有数据清零 */
template <typename Type>
inline void zerostruct (Type& structure) noexcept  
{
	memset (&structure, 0, sizeof (structure)); 
}

/** Delete an object pointer, and sets the pointer to null.
    Remember that it's not good c++ practice to use delete directly - always try to use a ScopedPointer
    or other automatic lifetime-management system rather than resorting to deleting raw pointers!
    删除一个对象指针,并且把它赋值为空
    Remember that it's not good c++ practice to use delete directly - always try to use a ScopedPointer
    or other automatic lifetime-management system rather than resorting to deleting raw pointers!
    最好不要直接去删除一个对象,应为C++的内存裸露使用时相当危险,最好使用封装过的例如ScopedPointer这样的
    或其他的自动寿命管理系统,不要直接去删除对象和清理指针
*/
template <typename Type>
inline void deleteAndZero (Type& pointer)                       
{
	delete pointer; pointer = nullptr;
}

/** A handy function which adds a number of bytes to any type of pointer and returns the result.
    This can be useful to avoid casting pointers to a char* and back when you want to move them by
    a specific number of bytes,
	按字节去移动指针
*/
template <typename Type, typename IntegerType>
inline Type* addBytesToPointer (Type* pointer, IntegerType bytes) noexcept
{
	return (Type*) (((char*) pointer) + bytes);
}

/** A handy function which returns the difference between any two pointers, in bytes.
    The address of the second pointer is subtracted from the first, and the difference in bytes is returned.
	计算两个指针之间的距离(字节数)
*/
template <typename Type1, typename Type2>
inline int getAddressDifference (Type1* pointer1, Type2* pointer2) noexcept 
{
	return (int) (((const char*) pointer1) - (const char*) pointer2); 
}

/** If a pointer is non-null, this returns a new copy of the object that it points to, or safely returns
    nullptr if the pointer is null.
	如果指针不为空,拷贝该指针的内容重新构建一个对象,返回对象指针
*/
template <class Type>
inline Type* createCopyIfNotNull (const Type* pointer)  
{
	return pointer != nullptr ? new Type (*pointer) : nullptr; 
}

为了使用方便,juce对这些操作内存的API稍微封装了一下,笔者加了一些中文注释,只要是玩过C++的朋友,应该都能看懂的,这里就不再赘述了。


然后,我们再看HeapBlock,这是一个管理内存块的智能指针,类似于Duilib中的CStdPtrArray数组指针,只不过这里面加入了泛型,在使用时可以清楚地知道是什么类型的指针,Duilib没有引入泛型是因为,CStdPtrArray已经定死了,它只用于CControlUI对象,所以一切从简了。这里只要对malloc,realloc,calloc之间的区别清楚,也非常容易看懂,malloc的功能是创建一块连续内存,calloc的功能是创建一块连续内存,然后将里面的数据初始化为0,realloc是改变一块现有的内存大小。还有一些比较绕弯的东西就是操作符重载,比如 operator  ElementType*()这种写法指的是,重写自身操作符,也就是说对象本身可以当做该函数的返回值,比如下面的例子

class A

public:

       opretor int()

      {

             return a;

      }  

private:

       int    a;

}


A* a = new A;     

int  b = *a+5; 

这是允许的


 还有另一种特殊的操作符重载,像这种ElementType* operator->()写法,不是简单的返回ElementType* 指针,而是使用返回的ElementType*来操作成员,示例如下

class A

public:

      void   go()

    {

    }

private:

       int    a;

}


class B

public:

    A* opreator->()

  {

       return a;

  }

private:

      A*  a;

}


B b;

b->go();   //就相当于(b.a)->go();

这样也是可以的


下面贴出带有中文注释的源码

juce_HeapBlock.h

template <class ElementType, bool throwOnFailure = false>
class HeapBlock
{
public:
    //==============================================================================
    /** Creates a HeapBlock which is initially just a null pointer.
	    创建一个heapblock对象
        After creation, you can resize the array using the malloc(), calloc(),
        or realloc() methods.
    */
    HeapBlock() noexcept  : data (nullptr)
    {
    }
    /** Creates a HeapBlock containing a number of elements.
	    创建一个Heapbolck对象,并开辟n块指定类型大小的连续空间
        The contents of the block are undefined, as it will have been created by a
        malloc call.

        If you want an array of zero values, you can use the calloc() method or the
        other constructor that takes an InitialisationState parameter.
    */
    explicit HeapBlock (const size_t numElements)
        : data (static_cast <ElementType*> (std::malloc (numElements * sizeof (ElementType))))
    {
        throwOnAllocationFailure();
    }
    /** Creates a HeapBlock containing a number of elements.
	      创建一个Heapbolck对象,并开辟n块指定类型大小的连续空间,并指定是否对这些空间清零
        The initialiseToZero parameter determines whether the new memory should be cleared,
        or left uninitialised.
    */
    HeapBlock (const size_t numElements, const bool initialiseToZero)
        : data (static_cast <ElementType*> (initialiseToZero
                                               ? std::calloc (numElements, sizeof (ElementType))
                                               : std::malloc (numElements * sizeof (ElementType))))
    {
        throwOnAllocationFailure();
    }
    /** Destructor.
	   对象析构,成员data指向的内存空间释放
        This will free the data, if any has been allocated.
    */
    ~HeapBlock()
    {
        std::free (data);
    }

   #if JUCE_COMPILER_SUPPORTS_MOVE_SEMANTICS
    HeapBlock (HeapBlock&& other) noexcept
        : data (other.data)
    {
        other.data = nullptr;
    }

    HeapBlock& operator= (HeapBlock&& other) noexcept
    {
        std::swap (data, other.data);
        return *this;
    }
   #endif
    //==============================================================================
    /** Returns a raw pointer to the allocated data.
	    自身重写操作符,使对象本身可以当做data成员指针来使用
        This may be a null pointer if the data hasn't yet been allocated, or if it has been
        freed by calling the free() method.
    */
    inline operator ElementType*() const noexcept{ return data; }
    /** Returns a raw pointer to the allocated data.
	    返回成员指针data
        This may be a null pointer if the data hasn't yet been allocated, or if it has been
        freed by calling the free() method.
    */
    inline ElementType* getData() const noexcept{ return data; }
    /** Returns a void pointer to the allocated data.
	    重写自身操作符,返回万能指针void*
        This may be a null pointer if the data hasn't yet been allocated, or if it has been
        freed by calling the free() method.
    */
    inline operator void*() const noexcept{ return static_cast <void*> (data); }
    /** Returns a void pointer to the allocated data.
	    重写自身操作符,返回const void*
        This may be a null pointer if the data hasn't yet been allocated, or if it has been
        freed by calling the free() method.
    */
    inline operator const void*() const noexcept{ return static_cast <const void*> (data); }
    /** Lets you use indirect calls to the first element in the array.
	    重写->操作符,使HeapBlock对象本身可以像data那样访问data的成员
        Obviously this will cause problems if the array hasn't been initialised, because it'll
        be referencing a null pointer.
    */
    inline ElementType* operator->() const  noexcept{ return data; }
    /** Returns a reference to one of the data elements.
	    重写[]操作符,使HeapLock对象可以像数组那样,访问指定位置的内存中的数据
        Obviously there's no bounds-checking here, as this object is just a dumb pointer and
        has no idea of the size it currently has allocated.
    */
    template <typename IndexType>
    inline ElementType& operator[] (IndexType index) const noexcept{ return data [index]; }
    /** Returns a pointer to a data element at an offset from the start of the array.
	    重写+操作符,向右移动成员data指针
        This is the same as doing pointer arithmetic on the raw pointer itself.
    */
    template <typename IndexType>
    inline ElementType* operator+ (IndexType index) const noexcept{ return data + index; }
    //==============================================================================
    /** Compares the pointer with another pointer.
	   重写==操作符,比较两个指针的位置是否相同
        This can be handy for checking whether this is a null pointer.
    */
    inline bool operator== (const ElementType* const otherPointer) const noexcept { return otherPointer == data; }
    /** Compares the pointer with another pointer.
        This can be handy for checking whether this is a null pointer.
    */
    inline bool operator!= (const ElementType* const otherPointer) const noexcept { return otherPointer != data; }
    //==============================================================================
    /** Allocates a specified amount of memory.

        This uses the normal malloc to allocate an amount of memory for this object.
        Any previously allocated memory will be freed by this method.
		
        The number of bytes allocated will be (newNumElements * elementSize). Normally
        you wouldn't need to specify the second parameter, but it can be handy if you need
        to allocate a size in bytes rather than in terms of the number of elements.

        The data that is allocated will be freed when this object is deleted, or when you
        call free() or any of the allocation methods.

		重新开辟内存
    */
    void malloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType))
    {
        std::free (data);
        data = static_cast <ElementType*> (std::malloc (newNumElements * elementSize));
        throwOnAllocationFailure();
    }

    /** Allocates a specified amount of memory and clears it.
	    重新开辟内存,并初始化为零
        This does the same job as the malloc() method, but clears the memory that it allocates.
    */
    void calloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType))
    {
        std::free (data);
        data = static_cast <ElementType*> (std::calloc (newNumElements, elementSize));
        throwOnAllocationFailure();
    }

    /** Allocates a specified amount of memory and optionally clears it.
	   重新开辟内存,初始化为零可选
        This does the same job as either malloc() or calloc(), depending on the
        initialiseToZero parameter.
    */
    void allocate (const size_t newNumElements, bool initialiseToZero)
    {
        std::free (data);
        data = static_cast <ElementType*> (initialiseToZero
                                             ? std::calloc (newNumElements, sizeof (ElementType))
                                             : std::malloc (newNumElements * sizeof (ElementType)));
        throwOnAllocationFailure();
    }

    /** Re-allocates a specified amount of memory.
	   使用relloc重新开辟内存
        The semantics of this method are the same as malloc() and calloc(), but it
        uses realloc() to keep as much of the existing data as possible.
    */
    void realloc (const size_t newNumElements, const size_t elementSize = sizeof (ElementType))
    {
        data = static_cast <ElementType*> (data == nullptr ? std::malloc (newNumElements * elementSize)
                                                           : std::realloc (data, newNumElements * elementSize));
        throwOnAllocationFailure();
    }

    /** Frees any currently-allocated data.
	   释放内存
        This will free the data and reset this object to be a null pointer.
    */
    void free()
    {
        std::free (data);
        data = nullptr;
    }

    /** Swaps this object's data with the data of another HeapBlock.
    	交换两个对象里面的成员data
        The two objects simply exchange their data pointers.
    */
    template <bool otherBlockThrows>
    void swapWith (HeapBlock <ElementType, otherBlockThrows>& other) noexcept
    {
        std::swap (data, other.data);
    }

    /** This fills the block with zeros, up to the number of elements specified.
	    数据清零
        Since the block has no way of knowing its own size, you must make sure that the number of
        elements you specify doesn't exceed the allocated size.
    */
    void clear (size_t numElements) noexcept
    {
	
			 zeromem (data, sizeof (ElementType) * numElements);
       
    }

    /** This typedef can be used to get the type of the heapblock's elements. */
    typedef ElementType Type;

private:
    //==============================================================================
    ElementType* data;

    void throwOnAllocationFailure() const
    {
        HeapBlockHelper::ThrowOnFail<throwOnFailure>::check (data);
    }

   #if ! (defined (JUCE_DLL) || defined (JUCE_DLL_BUILD))
    JUCE_DECLARE_NON_COPYABLE (HeapBlock)
    JUCE_PREVENT_HEAP_ALLOCATION // Creating a 'new HeapBlock' would be missing the point!
   #endif
};


HeapBlock可以这样使用:

     HeapBlock<int>  a =  malloc(3*sizeof(int));

     a[0] = 5;

     a[1] =10;

     a[2] =8;

     当a对象超出作用域时,a里面成员data所指向的内存会自动释放

     这是Juce中最简单的一个内存指针。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Skilla

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值