【问题标题】:a library forces global overloads of new/delete on me!图书馆强制我对 new/delete 进行全局重载!
【发布时间】:2011-01-01 17:05:05
【问题描述】:

我正在为一个大型闭源应用程序维护一个插件(实现为 dll)。这多年来一直运行良好。然而,随着其 SDK 的最新更新,供应商重载了全球运营商 new 和 delete。这给我带来了很多麻烦。发生的事情是我的插件分配了一个字符串。我将此字符串传递给一个静态链接库,该库对其进行修改(更改它的长度,从而重新分配它)。我的应用程序崩溃了。

原因当然是字符串位于供应商分配的自定义堆上。静态链接库对此堆一无所知,并尝试在该内存上使用默认的 new/delete 运算符。轰隆隆。

现在的问题是:如何保持我的代码干净并避免使用供应商的运算符?没有条件预处理器宏。我无法避免包含有问题的标头,因为它包含我需要的插件多 2000 行代码。我无法将提供的分配器传递到另一个库,因为它没有为此提供任何机制。我已经向供应商提出了这个问题。我不知道我还能尝试什么?

附录:经过一番激烈的辩论,我设法说服供应商再次从 SDK 的下一个版本中删除重载。我通过简单地破解当前的 SDK 并手动删除重载解决了我的直接问题。感谢此线程中的所有建议。它们充当了论据,并进一步“证明”了为什么重载首先是一个坏主意。

【问题讨论】:

  • 必须避免更改静态库吗?
  • 我可以在这种情况下更改它,因为它是开源的。但我真的很想避免在每个新版本中升级我的修改。我也更喜欢与我没有源访问权限的库一起使用的解决方案......
  • 另外,你说的是std::string、char*s还是其他类型的字符串?
  • 如果库重新分配您的字符串 并且 不提供对其分配器的访问权限,您将无能为力。继续烦他们,他们的图书馆坏了。
  • char * 在这种特殊情况下。但我认为这实际上无关紧要,因为问题仍然存在于所有分配中。

标签: c++ operator-overloading memory-management new-operator allocation


【解决方案1】:

如果您正在编译(通过包含头文件)覆盖的 new/delete 运算符,那么代码中对 new/delete 的所有调用都将使用它们。无法重新覆盖它(链接错误)或仅部分覆盖它等。

重写全局的 new/delete 操作符是一种糟糕的形式。这是个坏主意。如果你没有意识到为什么这是一个坏主意,你就没有资格这样做。如果您确实意识到为什么这是一个坏主意,那么您就有资格这样做,但您通常会选择不这样做。

在您希望人们直接包含在项目中的组件中,定义全局新/删除是成倍增加的邪恶。作为客户,您的工作是帮助供应商了解情况的严重性,或停止成为他们的客户。

您可以定义自定义分配器类型(参见link for a good tutorial,了解如何执行此操作、所需接口等)并将其专门用于您的 STL 类型(它是模板参数)。

对于 shared_ptr,您需要做一些不同的事情:如果您不希望默认的“删除 p”行为,它会将删除器对象作为构造函数的参数。这不是自定义分配器;它只是一个普通的一元仿函数。

【讨论】:

  • 您对被覆盖的 new 和 delete 卡住是正确的。但是,我不同意您的断言,即覆盖全局 new 和 delete 是不好的形式。我已经做到了,并且在其他人做过的几十个项目中工作过。作为一般经验法则,尽管您永远不会覆盖库中的 new 和 delete,因为这对您的用户来说可能会很痛苦。这就是为什么许多优秀的库都遵循 STL 约定,允许用户为对象指定自己的分配器。但是重写 new 和 delete 对于管理系统资源非常方便,您只需要知道自己在做什么。
【解决方案2】:

难道不能这样做吗:

namespace evil{

#include "evil_header.h"

}

然后 evil_header 声明为全局 new/delete 的内容变为 evil::new/evil::delete。我怀疑如果在 evil_header 中声明的事物的非标题定义,这会很好地发挥作用。

【讨论】:

    【解决方案3】:

    你可以在你的命名空间中使用另一个新的:

    namespace MyNS {
        // Declare your new/delete operators here
        // and also declare a class implementing the same interface as std::allocator
        // using your newly created memory management functions.
        // Don't forget to put all your classes in the namespace.
        // (if you don't have one already)
    }
    

    然后您可以使用所有 STL 类,将您的分配器类型作为模板参数。

    【讨论】:

    • 将 new/delete 放入您自己的命名空间并不能确保它们始终被使用(该命名空间中的类型通常在其他命名空间中引用)。事实上,它可能只会导致更多的分配器不匹配问题。定义用于 STL 类型的新分配器是一个正交问题,这种类型与全局 new/delete 无关,也不必位于任何特定的命名空间中。
    • 而且,自定义分配器不是从 std::allocator 派生的。请参阅我的答案中的链接。
    • 更正了关于继承的错误。然而,如果在命名空间中声明了 new/delete 运算符,则该命名空间中的所有内容都将使用它们。当然这并不完美(正如您所说,不在该名称空间中的其他类不会使用它们)。更好的是恢复原来的全局新的。重载时的一个好主意是使用与标准“nothrow”new 相同的重载技巧。
    【解决方案4】:

    一种选择是创建您自己的重载 new 运算符,该运算符可以根据 malloc 实现。

    这可以定义为:

    枚举 MyNew {EMyNew}; void *operator new(size_t size, MyNew);

    然后您可以将其称为MyClass* myClass = new (EMyNew)MyClass;

    由于这是根据 malloc 实现的,它的行为应该符合预期。唯一令人沮丧的是,您必须替换所有使用过 new 的实例。

    【讨论】:

    • 不幸的是,这不是一个选项,因为我不知道或控制“我使用过 new 的所有实例”。想想 STL、链接库等。
    • STL 可以通过使用自定义分配器来克服。
      链接库应该是安全的,因为它们不是针对您的头文件编译的。
    猜你喜欢
    • 2011-07-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-03
    • 1970-01-01
    • 2011-05-27
    • 1970-01-01
    相关资源
    最近更新 更多