我正在尝试为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))
。
这应该如何工作?
答案 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 }
这是您唯一显式调用析构函数的时间。