c++ - 我应该如何将 placement new 与自定义分配 API 一起使用?

标签 c++ c++11 memory-management placement-new

我正在处理一些具有自定义分配和删除的内存空间,这些内存空间是使用类似 malloc 的接口(interface)创建的,不受我的控制(即不透明的 C 风格函数,用于“分配 n 个字节”或“释放一个字节”分配的指针”)。所以,没有什么比 newdelete 更好的了。

现在,我想构造一个 T 的数组。我通过 auto space_ptr = custom_alloc(n*sizeof(T)) 获得了空间。现在我想做一些类似 array-placement-new 的事情来就地构造 n 元素。我该怎么做? ... 或者我应该从 1 循环到 n 并构建单个 T

注意:

  • 我在这里忽略对齐问题(或者更确切地说,假设 alignof(T) 划分 sizeof(T))。如果您想解决对齐问题,那会更好,但为简单起见,您可以忽略它。
  • 欢迎使用 C++11 代码(实际上是首选),但不要使用 C++14/17。

最佳答案

我假设您的内存力与您的 T 完全一致。您可能想检查一下。

下一个问题是异常。我们真的应该写两个版本,一个有构造会导致异常的可能性,一个没有。

我将编写异常安全版本。

template<class T, class...Args>
T* construct_n_exception_safe( std::size_t n, void* here, Args&&...args ) {
  auto ptr = [here](std::size_t i)->void*{
    return static_cast<T*>(here)+i;
  };
  for( std::size_t i = 0; i < n; ++i ) {
    try {
      new(ptr(i)) T(args...);
    } catch( ... ) {
      try {
        for (auto j = i; j > 0; --j) {
          ptr(j-1)->~T();
        }
      } catch ( ... ) {
        exit(-1);
      }
      throw;
    }
  }
  return static_cast<T*>(here);
}

和不异常(exception)的安全版本:

template<class T, class...Args>
T* construct_n_not_exception_safe( std::size_t n, void* here, Args&&...args ) {
  auto ptr = [here](std::size_t i)->void*{
    return static_cast<T*>(here)+i;
  };
  for(std::size_t i = 0; i < n; ++i) {
    new (ptr(i)) T(args...);
  }
  return static_cast<T*>(here);
}

你可以做一个基于标签分配的系统,根据是否从 Args&... 构造 T 抛出或不抛出,在它们之间进行选择。如果它抛出,并且 ->~T() 不平凡,请使用异常安全的。

C++17 公开了一些新函数来完成这些任务。他们可能会处理我不会处理的极端情况。


如果您尝试模拟 new[]delete[],如果 T 有一个非平凡的 dtor,您将拥有在 block 中嵌入多少 T

执行此操作的典型方法是在 block 的前面 为计数请求额外的空间。即,要求 sizeof(T)*N+K,其中 K 可能是 sizeof(std::size_t)

现在在您的 new[] 模拟器中,将 N 填充到第一位,然后在紧跟其后的 block 上调用 construct_n

delete[]中,传入的指针减去sizeof(std::size_t),读取N,然后销毁对象(从右到左到镜像构建顺序)。

所有这些都需要小心try-catch

但是,如果 ~T() 是微不足道的,那么您模拟的 new[]delete[] 都不会存储额外的std::size_t 他们也不会阅读它。

(请注意,这是如何模拟 new[]delete[]new[]delete[] 的工作取决于实现。我只是勾勒出一种你可以模拟它们的方法,它可能与它们在你的系统上的工作方式不兼容。例如,一些 ABI 可能始终存储 N,即使 ->~T() 是微不足道的,或者有无数其他变体。)


正如 OP 所指出的,您可能还想在处理上述问题之前检查是否存在琐碎的构造。

关于c++ - 我应该如何将 placement new 与自定义分配 API 一起使用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40472875/

相关文章:

c++ - 我应该如何将容器转换为 reference_wrappers 的容器?

C++ 电子邮件/SMTP

c++ - Signals2 connect() 使用模板

c++ - 为 std::vector<std::vector<TYPE>> 中的内部 vector 保留内存

c++ - cocos2d-X调用构造函数时调用retain

c++ - PropertyGrid 中的按钮

c++ - 为什么我不能用 std::unordered_map 替换 std::map

c++ - 如何从构造函数参数初始化模板成员数组?

go - 如何释放内存?

c - 在使用 realloc 之前是否需要释放指针变量?