自定义分配器,包括放置新案例

时间:2019-11-17 11:51:28

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

我正在尝试为C ++实现一个自定义分配器,该分配器可以在new / delete / malloc / free的任何形式下使用。

我的程序如何工作,我在程序开始时分配了一个x字节的内存池并使用它们。例如,当有人写orig时,我的程序将从可用的内存池中返回该地址并将其标记为已分配,并且该地址以及分配的大小将从内存池中删除。当有人写library(dplyr) library(purrr) d %>% group_split(orig) %>% #Try `map` if you need the ouptput as a list map_df(~.x %>% mutate(x_trans = filter(d, dest==.x$orig[1]) %>% pull(x))) # A tibble: 16 x 4 orig dest x x_trans <chr> <chr> <dbl> <dbl> 1 A A -0.626 -0.626 2 A B 0.184 0.330 3 A C -0.836 0.576 4 A D 1.60 -0.621 5 B A 0.330 0.184 6 B B -0.820 -0.820 7 B C 0.487 -0.305 8 B D 0.738 -2.21 ... 时,该地址将返回到内存池中并可以再次使用。

我的问题是我不完全了解int* a= new int;的工作方式以及我该如何处理,因为调用我的函数以在new / malloc上分配内存时,我只有程序需要的内存大小作为参数,而我只是将一个可用地址返回给该内存以使用。考虑以下示例

delete a;

在第一行,我的自定义分配器将从内存池中返回一个地址,其中内存中的可用内存总计为new(placement),在第三行中,我的自定义分配器将再次分配内存,返回另一个地址。当我打印auto p = (std::string*)malloc(5 * sizeof(std::string)); void * placement = p; new(placement) std::string(4, (char)('a')); std::cout<< *p; 时,它会完全打印出我期望的5* sizeof(std::string))

这应该如何工作?

1 个答案:

答案 0 :(得分:2)

普通的new做两件事:

  • 分配存储;和

  • 构造一个对象。

现在,我们要将这两个步骤分开。分配原始存储很容易,但是在C ++中,没有“本机”方式在给定地址处构造对象。因此,通过返回第一步的给定指针,new运算符被重载以达到此目的。

我们不需要相应的delete,因为我们可以手动调用析构函数。在C ++ 17中,std::destroy_at已添加到标准库中。从C ++ 20开始,std::construct_at可用于构造对象,而不是放置new:

std::construct_at(p, 4, 'a');

C++ Super-FAQ很好地解释了新的展示位置:

什么是“新的展示位置”,为什么我要使用它?

  

有很多新的用途。最简单的用途是放置一个   对象位于内存中的特定位置。这是通过提供   该位置作为指向新表达式新部分的指针参数:

#include <new>        // Must #include this to use "placement new"
#include "Fred.h"     // Declaration of class Fred
void someCode()
{
  char memory[sizeof(Fred)];     // Line #1
  void* place = memory;          // Line #2
  Fred* f = new(place) Fred();   // Line #3 (see "DANGER" below)
  // The pointers f and place will be equal
  // ...
}
     

第1行创建了sizeof(Fred)个字节的内存数组,该数组是   足以容纳Fred对象。第2行创建了一个指针place   指向此内存的第一个字节(遇到C   程序员将注意到这一步骤是不必要的;只有那里   使代码更明显)。第3行实际上只是调用   构造函数Fred::Fred()this中的Fred指针   构造函数将等于place。返回的指针f将   因此等于place

     

建议:除非必须,否则请不要使用这种“新放置”语法。仅当您确实关心将对象放置在   内存中的特定位置。例如,当您的硬件具有   内存映射的I / O计时器设备,并且您想放置一个Clock对象   在那个内存位置。

     

危险:您独自承担传递给“ placement new”运算符的指针指向内存区域的责任。   足够大,并已根据您所使用的对象类型正确对齐   创造。编译器和运行时系统都不做任何事情   尝试检查您是否这样做正确。如果您的Fred班级   需要在4字节边界上对齐,但是您提供了一个位置   排列不正确,可能会给您带来严重的灾难   手(如果您不知道“对齐”是什么意思,请   展示位置新语法)。您已被警告。

     

您还独自负责破坏放置的对象。   这是通过显式调用析构函数来完成的:

void someCode()
{
  char memory[sizeof(Fred)];
  void* p = memory;
  Fred* f = new(p) Fred();
  // ...
  f->~Fred();   // Explicitly call the destructor for the placed object
}
     

这是您唯一显式调用析构函数的时间。