库中的新运算符和删除运算符替代

New and delete operators override in libraries

本文关键字:运算符 删除      更新时间:2023-10-16

如果两个库(动态链接)有自己的全局重写版本的newdelete运算符,并且它们使用自己的内存管理,会发生什么?

在库中提供内存管理设施通常是错误的吗?或者在某些情况下,只为某些特定类提供内存管理,只定义特定于类的newdelete运算符覆盖是否是好的?

静态链接库的情况有什么不同吗?

通常,这被标记为"这里有龙"。这取决于各种各样的事情。通常,这两个库会发生冲突,新建和删除最终会被其中一个库覆盖——这是你所能期望的最好结果。

备选方案:

  • 库A启动。覆盖新建/删除,分配一些内存。库B启动并覆盖。在系统关闭时,库A的内存会随着库B的删除而释放。这不好。

  • 库A中分配的内存使用库A的覆盖,库B也是如此。如果最终库A中的内存被库B释放,那么您将失败。(这可能更令人困惑,因为如果B删除的对象有一个虚拟析构函数,那么删除可能最终由a完成……所以它是有效的。)

我认为Martin很好地回答了你的问题,他概述了发生了什么,也没有什么是有点危险和不太可取的(确实有龙)。让我通过提供一个替代方案来扩展它:避免覆盖new/delete,而是使用分配器的概念。

例如,如果您查看std::vector,您会注意到它存储的类型和分配器都是模板化的。通过编写一个一致的分配器,您可以精确地控制std::vector如何分配和取消分配内存。请注意这是多么的好和松散耦合:即使不能更改任何原始的std::vector源代码,也可以轻松地对内存分配进行完全控制。

如果你想让图书馆B以特定的方式进行分配,我会做的是:

  1. 以std::allocater的方式编写一个符合分配器概念的分配器
  2. 确保所有直接使用动态内存分配的类(例如,不通过其成员之一)都具有分配器意识
  3. Typedef所有这些类,以便它们在默认情况下使用您的分配器

为了澄清步骤3,我的意思是在库的基本头文件中写这样的东西:

template <class T>
using my_lib::vector = std::vector<T, my_lib::MyAllocator<T>>;

现在,您可以在库中的任何位置使用"vector",这将是一个正常的向量,但使用您的分配方案。

步骤1的范围从非常简单(对于无状态分配器)到非常棘手(有状态分配器有很多问题)。至于步骤2,容器的动态内存非常简单,因为标准库中的所有容器都已经支持这一点。但是,如果您使用动态内存来实现多态性,则需要做一些额外的工作(可能需要编写一个合适的包装器),才能以分配器感知的方式实现这一点。

如果有人有很好的例子说明为什么你想覆盖new/delete而不是使用分配器(例如,因为有一些事情你不能用分配器),我很想听听他们的意见。

编辑:请注意,如果同时使用库A和库B,则不会有任何问题,因为没有覆盖全局运算符。