在DLL中分配内存并将其指针提供给客户端应用程序,这是不是一种糟糕的做法

Is it bad practice to allocate memory in a DLL and give a pointer to it to a client app?

本文关键字:是不是 一种 应用程序 内存 分配 DLL 客户端 指针      更新时间:2023-10-16

我使用的是一个动态加载DLL的exe。DLL中的函数在堆上分配内存,并将指向该内存的指针传递给exe。

一位大四学生说,这样做是不好的做法。他说,如果我必须在exe和DLL之间共享内存,exe必须分配内存并将指向内存的指针传递给DLL,反之亦然。这是真的吗?为什么?

编辑:在我的情况下,我计划在DLL内部分配和释放内存。

以下是让调用者提供指针的一些原因:

  1. 对称所有权语义其他几个答案已经对此进行了解释
  2. 避免分配器和解除定位器不匹配正如Aesthet的回答中所提到的,如果DLL分配了一个指针并返回它,调用者必须调用相应的deallocator来释放它。这并不一定是微不足道的:DLL可能静态链接到一个版本的malloc/free,而.exe则链接到不同版本的malloc/free。(例如,DLL可能使用发布版本,而.exe则使用专门的调试版本。)
  3. 灵活性如果DLL是通用的,那么让调用者分配内存会给调用者更多的选择。假设调用者不想使用malloc,而是希望从某个特定的内存池中分配内存。也许在这种情况下,调用者可以提供一个指向堆栈上分配的内存的指针。如果DLL本身分配了内存,则调用方没有任何这些选项

(第二点和第三点也主要可以通过让.exe为DLL代码提供分配器/解除定位器来解决。)

设计模式背后的一个基本思想是所有权。这个想法是-one who creates a resource (and thereby holds it in the pointer) should be responsible for deleting the resource。这将确保设计的神圣性,在项目的更长寿命中,开发人员可以看到更少的错误。

所以现在在你的情况下,DLL可以由任何可执行文件附加,他可以尝试删除资源,这可能会导致未来的问题。因此,我认为有人建议反之亦然,我认为这是一个合理的建议。

我以前见过这个问题,它是由DLL和exe与CRT的链接不同(静态、动态MT等)引起的。

我要在DLL和可执行文件之间传递一个指向内存的指针,它们都应该提供某种Free()功能,以从各自的堆中释放内存。

通常,堆(the One heap)属于进程,从哪里分配并不重要,所以这会很好地工作,除非它不是

因此,认为这是"不良做法"的说法是正确的。没有什么事情比正常工作的东西更糟糕,除非它不正常。

最糟糕的是,当一切都爆发时,并不是很明显出了什么问题,你很容易在不知情的情况下遇到问题。这可能是一件很容易的事情,就像把一个特定版本的CRT链接到你们的DLL中一样。或者您团队中的某个人可能出于某种原因创建了一个单独的堆。或者,其他一些不太明显的原因导致了另一个堆的创建。

没有错误堆的情况之所以如此恶劣,是因为你通常不知道会发生什么,也不知道什么时候(或者是否有人会注意到)。

您可以从堆函数异常中获得NULL返回值。您的应用程序可能已经为其中任何一个做好了准备,也可能没有做好准备(老实说,您总是检查返回值,不是吗?)。它可能会在释放后立即崩溃,或者你可能只是默默地泄漏内存,几分钟或几个小时后地址空间(或内存)用完,没有人知道原因。或者,其他什么
因此,您所看到的可能与问题的原因根本没有(明显的)相关性。

我只想指出,将分配器传递到DLL以分配内存是完全安全的,并且是C++std库本身使用的标准模型。在这种情况下,DLL通过调用方传递的方法来完成分配,避免传递指针,并避免链接到malloc()的不同实现的问题。

exe和dll可能有不同的堆。当其中一个尝试释放另一个分配的内存时,释放失败,出现泄漏。

只有当exe和dll都使用CRT时,它们才使用相同的堆。

因此,在相同的二进制文件中进行分配和释放是一个非常好的建议。

正如@kumar_m_kiran已经指出的,这是关于所有权问题,但我想注意的是,@aleguna也是正确的,因此,imho,正确的规则是"在DLL或EXE中分配和释放相同的内存,但不能同时在两者中"。

我认为,放弃原始指针通常是不好的做法。在dll的情况下,它应该返回一个智能指针,该指针具有适当处理清理的自定义删除程序(选项按首选项顺序为std::unique_ptrstd::shared_ptrboost::shared_ptr)。

事实上,使用std::weak_ptrboost::weak_ptr可能更安全——这需要您在每次访问资源时进行检查,因为dll可能在此期间已卸载。

这不一定是糟糕的做法,但这是危险的做法(可能也是糟糕的)。你需要仔细思考谁应该负责释放记忆。exe可能无法(或不应该)直接释放DLL的内存,因此可能会在稍后将此指针传递回DLL。所以现在我们在EXE和DLL之间来回传递指针,这并不好。

我会说不,这不是"糟糕的做法"。根据我的经验,你只需要小心在正确的内存空间中释放指针。我已经建立了一个图形引擎,在多个DLL中分配资产(每个DLL代表一个迷你游戏);使用shared_ptr(不过,在当时它是Boost,我相信C++11(或更新版本)std::shared_ptr支持相同的语义)。我会提供一个函数,删除正确空间中的内存。在这一点上,主要关注的是确保在释放DLL之前释放shared_ptrs。我现在记不清了,但我们可能使用了DLL/DLL包装器拥有的shared_ptr列表,指针的所有其他用途都是通过一个weak_ptr TO共享d_ptr。