IMGUI通读05:内存分配

阅读量: 鲁文奎 2021-04-22 12:04:30
Categories: Tags:

[toc]

介绍

内存分配模块在imgui.h中定义, 包含以下主要函数:

struct ImNewDummy {};
inline void* operator new(size_t, ImNewDummy, void* ptr) { return ptr; }
inline void  operator delete(void*, ImNewDummy, void*) {} // This is only required so we can use the symmetrical new()

#define IM_ALLOC(_SIZE)                     ImGui::MemAlloc(_SIZE)
#define IM_FREE(_PTR)                       ImGui::MemFree(_PTR)
#define IM_NEW(_TYPE)                       new(ImNewDummy(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE
template<typename T> void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p); } }

如上,ImGui 通过重载new函数,重新定义new的使用方式, 并以此追踪内存泄漏问题。

内存分配结构剖析

C++ 中默认的new函数

_Ret_notnull_ _Post_writable_byte_size_(_Size)
_VCRT_ALLOCATOR void* __CRTDECL operator new(
    size_t _Size
    );

_Ret_maybenull_ _Success_(return != NULL) _Post_writable_byte_size_(_Size)
_VCRT_ALLOCATOR void* __CRTDECL operator new(
    size_t _Size,
    std::nothrow_t const&
    ) noexcept;

_Ret_notnull_ _Post_writable_byte_size_(_Size)
_VCRT_ALLOCATOR void* __CRTDECL operator new[](
    size_t _Size
    );

_Ret_maybenull_ _Success_(return != NULL) _Post_writable_byte_size_(_Size)
_VCRT_ALLOCATOR void* __CRTDECL operator new[](
    size_t _Size,
    std::nothrow_t const&
    ) noexcept;

因此, 不允许重载这个版本

void* operator new(size_t, void*);

所以, 增加一个ImNewDummy参数来规避此问题,如下:

#define IM_NEW(_TYPE)                       new(ImNewDummy(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE

同时, 在MemAlloc函数中, 我们可以统计内存分配大小、追踪内存泄漏等问题

void* ImGui::MemAlloc(size_t size)
{
	cout << "call MemAlloc " << size << endl;
	return GImAllocatorAllocFunc(size, GImAllocatorUserData);
}

void ImGui::MemFree(void* ptr)
{
	cout << "call MemFree " << endl;
	return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
}

真正的内存分配函数是由GImAllocatorAllocFunc来决定, GImAllocatorUserData是一个用户内存统计数据, 暂时没有什么需要注意的地方

static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
static void(*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
// 记录分配相关的数据
static void*    GImAllocatorUserData = NULL;

如上, 我们可以看到, GImAllocatorAllocFunc和GImAllocatorFreeFunc都是函数指针, 指向一个默认的分配器和释放器

// Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds.
#define IM_UNUSED(_VAR)             ((void)_VAR)

static void*   MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
static void    FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }

其实, 最终就是我们熟悉的malloc函数。

IM_PLACEMENT_NEW

通俗点说,此函数就是在已有的内存空间上重新构造一个相同类型的对象

	Person* p2 = IM_NEW(Person)();
	Person* p3 = IM_PLACEMENT_NEW(p2) Person(1001, "李四");

内存地址:

模板函数IM_DELETE 和 IM_FREE区别

区别如下:

因此, 建议使用IM_DELETE函数

完整测试代码

#include<iostream>
#include<string>

using namespace std;

// Used to silence "unused variable warnings". Often useful as asserts may be stripped out from final builds.
#define IM_UNUSED(_VAR)             ((void)_VAR)

static void*   MallocWrapper(size_t size, void* user_data) { IM_UNUSED(user_data); return malloc(size); }
static void    FreeWrapper(void* ptr, void* user_data) { IM_UNUSED(user_data); free(ptr); }

static void* (*GImAllocatorAllocFunc)(size_t size, void* user_data) = MallocWrapper;
static void(*GImAllocatorFreeFunc)(void* ptr, void* user_data) = FreeWrapper;
// 记录分配相关的数据
static void*    GImAllocatorUserData = NULL;

namespace ImGui
{
	void*         MemAlloc(size_t size);
	void          MemFree(void* ptr);
}

void* ImGui::MemAlloc(size_t size)
{
	cout << "call MemAlloc " << size << endl;
	return GImAllocatorAllocFunc(size, GImAllocatorUserData);
}

void ImGui::MemFree(void* ptr)
{
	cout << "call MemFree " << endl;
	return GImAllocatorFreeFunc(ptr, GImAllocatorUserData);
}

struct ImNewDummy {};
// void* operator new(size_t, void*); // C++中, 不允许重新定义这个版本 
inline void* operator new(size_t, ImNewDummy, void* ptr) { return ptr; }
inline void  operator delete(void*, ImNewDummy, void*) {} // This is only required so we can use the symmetrical new()

#define IM_ALLOC(_SIZE)                     ImGui::MemAlloc(_SIZE)
#define IM_FREE(_PTR)                       ImGui::MemFree(_PTR)
#define IM_NEW(_TYPE)                       new(ImNewDummy(), ImGui::MemAlloc(sizeof(_TYPE))) _TYPE
#define IM_PLACEMENT_NEW(_PTR)              new(ImNewDummy(), _PTR)
template<typename T> void IM_DELETE(T* p) { if (p) { p->~T(); ImGui::MemFree(p); } }

class Person {
public:
	Person();
	Person(int id, string name);
	~Person();
	int Id; //4
	string name; // 28
	void printInfo();
};

Person::Person()
{
	this->Id = 0;
	this->name = "";
}

Person::Person(int id, string name) 
{
	this->Id = id;
	this->name = name;
}

Person::~Person()
{
	cout << "call destructor func... " << endl;
}

void Person::printInfo()
{
	cout << "the person's id is " << this->Id << this->name << endl;
}

void main()
{
	// test 1 - new 和 free
	//Person* p1 = new Person(1000, "张三");
	//p1->printInfo();
	//free(p1);

	// test 2 - IM_NEW 和 IM_FREE
	/*Person* p1 = IM_NEW(Person)(1000, "张三");
	p1->printInfo();
	IM_FREE(p1);*/

	// test 3 - IM_NEW 和 IM_PLACEMENT_NEW
	Person* p2 = IM_NEW(Person)();
	Person* p3 = IM_PLACEMENT_NEW(p2) Person(1001, "李四");

	// test 4 - 多次FREE
	// IM_FREE 仅回收内存
	//IM_FREE(p2);
	//IM_FREE(p2);

	// test 5 - IM_DELETE
	// IM_DELETE 调用析构函数 && 内存回收
	IM_DELETE(p2);
}