我正在使用动态加载 DLL 的 exe。DLL 中的函数在堆上分配内存并将指向该内存的指针传递给 exe。
一位资深人士表示,这样做是不好的做法。他说,如果我必须在 exe 和 DLL 之间共享内存,则 exe 必须分配内存并将指向该内存的指针传递给 DLL,反之亦然。这是真的?为什么?
编辑:就我而言,我计划在 DLL 本身内部分配和释放内存。
我正在使用动态加载 DLL 的 exe。DLL 中的函数在堆上分配内存并将指向该内存的指针传递给 exe。
一位资深人士表示,这样做是不好的做法。他说,如果我必须在 exe 和 DLL 之间共享内存,则 exe 必须分配内存并将指向该内存的指针传递给 DLL,反之亦然。这是真的?为什么?
编辑:就我而言,我计划在 DLL 本身内部分配和释放内存。
以下是让调用者提供指针的一些原因:
malloc
/free
则与/.exe
的不同版本链接。(例如,DLL 可能使用发布版本,而 DLL使用专门的调试版本。)malloc
free
.exe
malloc
,而是希望从某个特定的内存池中分配内存。也许这是调用者可以提供指向堆栈上分配的内存的指针的情况。如果 DLL 自己分配内存,则调用者没有这些选项中的任何一个。(第二点和第三点也主要可以通过.exe
提供一个分配器/释放器供 DLL 代码使用来解决。)
设计模式背后的基本思想之一是所有权。这个想法是 - one who creates a resource (and thereby holds it in the pointer) should be responsible for deleting the resource
。这将确保设计的神圣性,并且在项目的更长生命周期中,其开发人员可以看到更少的错误。
所以现在在你的情况下,任何可执行文件都可以附加 DLL,他可以尝试删除资源,这可能会导致未来的问题。所以我认为反之亦然,我认为这是一个合理的建议。
我以前见过这个问题,它是由链接到 CRT(静态、动态 MT 等)的 DLL 和 exe 引起的。
我将在 DLL 和可执行文件之间传递一个指向内存的指针,它们都应该提供某种Free()
功能来从各自的堆中释放内存。
通常,堆(The One Heap)属于进程,从哪里分配都没有关系,所以这可以正常工作,除非它不是。
因此,声称这是“不良做法”是有效的。很少有事情比可以正常工作的事情更糟糕,除非它不能正常工作。
最糟糕的是,当一切都崩溃时,并不能立即看出哪里出了问题,而且您很容易在不知不觉中遇到问题。这可能就像将特定版本的 CRT 链接到您的 DLL 一样简单。或者您团队中的某个人可能出于某种原因创建了一个单独的堆。或者,其他一些不是立即显而易见的原因导致另一个堆被创建。
使 free-from-wrong-heap 情况如此恶劣的原因在于,您通常不知道会发生什么,或何时(或是否有人会注意到)。
您可能会从堆函数或异常中获得 NULL 返回值。您的应用程序可能已经为它们中的任何一个做好了准备,也可能没有(老实说,您总是检查返回值,不是吗?)。它可能会在释放后立即崩溃,或者您可能只是默默地泄漏内存并在几分钟或几小时后耗尽地址空间(或内存),没人知道为什么。或者是其他东西。
因此,您所看到的可能与导致问题的原因(明显)不相关。
我只想指出,将分配器传递给 DLL 以分配内存是非常安全的,并且是 C++ std 库本身使用的标准模型。在这种情况下,分配是由 DLL 通过调用者传递的方法完成的,避免传递指针,并避免链接到不同实现的问题malloc()
。
exe 和 dll 可能有不同的堆。当任何一个尝试释放另一个分配的内存时,释放失败并且存在泄漏。
仅当 exe 和 dll 使用相同版本的 CRT 动态使用 CRT 时,它们才使用相同的堆。
所以在同一个二进制文件中进行分配和释放是一个很好的建议。
我认为交出原始指针通常是不好的做法。如果是 dll,它应该返回一个智能指针,该指针具有一个自定义删除器,可以适当地处理清理(选项是std::unique_ptr
,std::shared_ptr
或boost::shared_ptr
按优先顺序排列)。
事实上,使用它可能更安全,std::weak_ptr
或者boost::weak_ptr
- 这些要求您在每次要访问资源时进行检查,因为此时 dll 可能已被卸载。
正如@kumar_m_kiran 已经指出的那样,这是关于所有权问题,但是我想指出,@aleguna 也是正确的,因此,恕我直言,正确的规则是“在 DLL 或 EXE 中分配和释放相同的内存,但不是在两者中” .
这不一定是不好的做法,但它是危险的做法(而且可能是坏的)。您需要仔细考虑谁负责释放内存。exe 可能无法(或不应该)直接释放 DLL 的内存,因此可能会在以后将此指针传递回 DLL。所以现在我们在一个 EXE 和一个 DLL 之间来回传递一个指针,这不是很好。
I'd say no, it's not "bad practice". From my experience, you just need to be careful that you release that pointer in the right memory space. I've built a graphics engine that allocated assets in multiple DLLs (each DLL represented a mini game); using a shared_ptr (at the time it was Boost, however, I'm sure the C++11 (or newer) std::shared_ptr supports the same semantics). I would supply a function that would delete the memory in the right space. The main concern, at this point, is that you ensure you free your shared_ptrs before you free the DLL. I can't recall now, but we might have used a list of shared_ptrs owned by the DLL/DLL wrapper, and all other uses of the pointer were through a weak_ptr TO that shared_ptr.