为DLL重载new和delete运算符

5

如何为 dll 重载 newdelete 运算符。我已经在 dll 中编写了重载的运算符,但与该 dll 链接的客户端没有使用 重载的 new 和 delete


请看这个问题:https://dev59.com/4VbUa4cB1Zd3GeqPAJuV - Felice Pollano
1
显而易见的答案是“不要这样做”。你是仅在自己的类上覆盖newdelete,还是试图在应用程序中全局重新定义它们?你能否重建客户端应用程序,或者你尝试修改预构建二进制文件的行为? - Rook
2个回答

9
这里是C++标准对此的解释,位于第17.6.4.6/3节:
程序使用其定义的(new/delete运算符)而不是实现提供的默认版本。这种替换发生在程序启动之前。程序的定义不得指定为inline。无需诊断。
仔细阅读后,可以清楚地解释你遇到的问题。这里存在一种“进退两难”的情况。
一方面,你不能在DLL中编译新/删除运算符的定义,因为重载的new/delete不能被动态链接(这是因为在静态初始化期间可能需要new/delete,而在加载DLL之前和之后会有不一致的new/delete运算符,这是未定义的行为)。
另一方面,您不能仅将新的/删除操作符定义放在DLL头文件中,因为它们需要被标记为inline才能满足一次定义规则(ODR),而这反过来又不能满足上述条款。不将它们标记为inline的要求可能是因为标记为inline的函数定义具有“无链接”,导致每个翻译单元使用其自己编译的版本(或作为内联扩展),通常没问题,但对于动态内存分配则不然。
以上两种情况都是出于正确性的考虑,通常需要保证使用new分配的内存使用相应的delete操作符进行释放(即“编译在一起”,可以这么说,或者都默认)。例如,如果您的新/删除操作符依赖于底层的malloc/free调用,则依赖于调用新/删除操作符的翻译单元所使用的堆,在DLL和可执行文件之间,不能保证此堆将是相同的(实际上,在特定情况下,在Windows中,两个模块使用两个单独的堆进行动态内存分配)。
因此,正如Rook所说,你的问题的解决方案是“不要那样做”。不要为DLL对象过载new/delete运算符,因为无论你如何扭曲你的代码,都没有一种干净的方法来正确地完成这个任务,它总会归结为上面所述的同一个问题。
相反,你应该使用工厂函数模式来创建DLL对象,并返回一个智能指针(例如std::shared_ptr),其中包含一个自定义删除器,该删除器依赖于将删除操作动态分派回对象创建的位置。这是受Chad Austin技术启发的一种方法。我在这里做了非常类似的事情。

Mikael,我正在阅读有关不能将定义放在DLL头文件中的部分,因为它们需要被标记为inline以满足ODR。这难道不会满足ODR吗?在头文件中但在类之外定义该定义,例如 MyClass:: SomeDefinition() {...}。这不会是内联的,并且会满足ODR,不是吗? - clanmjc
@clanmjc 亲自试一下就知道了。如果您在头文件中定义了类声明之外的成员函数,则它将正确编译,但是链接器在尝试链接使用该函数的多个对象文件时会出现ODR错误。为解决此问题,需要使用 inline 标记该定义,这与在类声明中定义它相同。因此,无论是将其放入类声明中还是显式标记为 inline,都需要将其标记为 inline。两种方法都是完全等效的。 - Mikael Persson

0
你可以尝试编写自己的malloc和delete函数,然后创建一个C定义,基本上包含覆盖new和delete的代码,并调用这些自定义的malloc和delete函数。
这样你就可以做一些像这样的事情:
MODULE_START()
// CODE HERE

MODULE_END()

这个应该能顺利运行。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接