问题背景
公司里出现了这么个亟待解决的问题,就是项目中的多个动态库在释放时,由于顺序不当产生使用已析构对象野指针引用问题,导致系统崩溃。因此,个人认为解决这个难题的前提是使用统一的动态库负责内存分配与回收。
注释:对于所有DLL都是以MD编译模式生成,则不存在内存于A堆创建,B堆释放的风险。DLL统一分配仅仅适用于MT编译的模块代码。
设计思路
统一内存管理模块包括:内存分配模块(PMemory.dll),动态库加载模块(PExport.lib),业务模块(PService.exe或dll),其中
PMemory.dll
内存统一分配单元,提供统一的new与delete接口。
PExport.lib
封装动态加载与释放PMemory.dll的逻辑,简化代码编写工作。
PService.[exe/dll/lib]
具体业务逻辑单元。
实现代码
PMemory.dll
#include "PMemory.h"
#include <stdlib.h>
void* p_new(size_t size)
{
return ::malloc(size);
}
void p_delete(void* address)
{
::free(address);
}
PExport.lib
#include "PExport.h"
#include <Windows.h>
#include <stdlib.h>
class _Initial
{
public:
using FunctionNew = void* (*)(size_t);
using FunctionDelete = void (*)(void*);
public:
_Initial() : m_Handle(nullptr), m_FunctionDelete(nullptr), m_FunctionNew(nullptr)
{
m_Handle = ::LoadLibraryA("PMemory.dll");
if (m_Handle)
{
m_FunctionNew = (FunctionNew) ::GetProcAddress(m_Handle, "p_new");
m_FunctionDelete = (FunctionDelete) ::GetProcAddress(m_Handle, "p_delete");
}
}
~_Initial()
{
if (m_Handle)
{
m_FunctionNew = nullptr;
m_FunctionDelete = nullptr;
::FreeLibrary(m_Handle);
m_Handle = 0;
}
}
void* _new(size_t size)
{
if (m_FunctionNew)
{
return m_FunctionNew(size);
}
return nullptr;
}
void _delete(void* address)
{
if (m_FunctionDelete)
{
m_FunctionDelete(address);
}
}
private:
HMODULE m_Handle;
FunctionNew m_FunctionNew;
FunctionDelete m_FunctionDelete;
};
_Initial g_Memory;
void* p_malloc(size_t size)
{
return g_Memory._new(size);
}
void p_free(void* address)
{
g_Memory._delete(address);
}
void p_delete(void* address)
{
g_Memory._delete(address);
}
PService.exe
#include <PExport.h>
class A
{
public:
A(int n, int m) : a(n), b(m) {}
int a = 123;
int b = 321;
};
void main()
{
auto a = p_new<A>(666, 999);
printf("%d %d", a->a, a->b);
p_delete(a);
}
运行截图
总结
至此,虽然开头提及因对象依赖关系不当而导致野指针引用的问题仍然没有得到解决,但却离成功更近了一步。现在能够保证,在任何动态库之间使用接口创建或回收内存,通常不会系统报错。