gpt4 book ai didi

c++ - 如何在可能使用 placement new 的同时创建数组

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:45:26 25 4
gpt4 key购买 nike

我一直致力于创建一个自定义分配器作为一项有趣的练习/实践,我在创建数组时遇到了两个潜在的问题。对于典型的分配调用,我将使用 mallocplacement new。但是,当我去创建一个数组时,我对应该如何完成感到困惑。有一次,我注意到在某些地方似乎 placement new 对于数组(例如 here)可能不安全.在尝试对数组使用 placement new 时,我也遇到了自己的错误。我会得到错误

`error C2679: binary '=' : no operator found which takes a right-hand operand of type 'SomeClass *' (or there is no acceptable conversion)

我理解错误(我相信),但我更愿意通过我的数组构造方法解决错误。我有两个问题

1) 分配器如何在不使用 new[] 的情况下创建数组?是 placement new 吗?如果是这样,我上面发布的链接中提到的潜在危险是什么?

2) 如果我想使用 placement new 并在数组中的每个元素上调用它,为什么会出现上述错误?

#include <stdio.h>
#include <new>

class SomeClass{
public:
SomeClass() {
printf("Constructed\n");
}

~SomeClass() {
printf("Destructed\n");
}
};

void* SomeAllocationFunction(size_t size) {
return malloc(size);
}

template<typename Type>
Type* SomeArrayAllocationFunction(size_t count){
Type* mem = (Type*)SomeAllocationFunction(sizeof(Type) * count);

for(unsigned int i = 0; i < count; ++i)
{
mem[i] = new(mem + i) Type();
}

return mem;
}

int main(void){
SomeClass* t = SomeArrayAllocationFunction<SomeClass>(2);
}

最佳答案

1) How can an allocator create an array without using new[]? Is it with placement new? If so, what about the potential dangers mentioned from the link I posted above?

链接中的问题是误解了事情的运作方式。每个实现都有一个实现定义的方法来记录有关已分配数组的信息。单个对象不需要此信息,因为它由客户端通过 delete 的实现进行管理。

对于数组,实现必须记录诸如元素计数、要调用的析构函数(如果适用)、元素大小……这些东西通常存储在返回分配的开始处,实现显然会偏移数组的大小分配请求适当。因此,实际大小被偏移以容纳这些隐藏值。这就是为什么 malloc(sizeof...) 不会工作,除非你的分配器做额外的簿记(顺便说一句,std::allocator 和集合接口(interface)带来)。

要正确记录此信息,您可以定义 static void* operator new[]。要通过放置将您自己的分配器合并到该方案中,您可以使用以下方法:

// quick/dirty/incomplete illustration:
#include <stdio.h>
#include <new>
#include <cstdlib>

class t_allocator {
public:
t_allocator() {
}

~t_allocator() {
}

public:
void* allocate(const size_t& size) {
return malloc(size);
}
};

class SomeClass {
public:
SomeClass() {
printf("Constructed\n");
}

~SomeClass() {
printf("Destructed\n");
}

public:
static void* operator new[](size_t size, t_allocator& allocator) {
return allocator.allocate(size);
}

/* in case static void* operator new[](size_t size, t_allocator& allocator) throws: */
static void operator delete[](void* object, t_allocator& allocator) {
/* ... */
}

static void operator delete[](void* object) {
/* matches t_allocator::allocate */
free(object);
}
};

int main(void) {
t_allocator allocator;
SomeClass* t(new (allocator) SomeClass[2]);

delete[] t;
t = 0;

return 0;
}

请注意,如果您的分配器可能抛出异常,您将类似地实现放置 operator delete[]

如果你想让你的分配器做一些簿记,它会变得困惑。就个人而言,我认为这种情况并没有被语言很好地实现,尤其是因为数组初始化没有很好地实现。总会有一个额外的步骤来执行附近的 build 或破坏,或者一些全局可访问的数据在这种情况下使用。

2) If I am suppose to use placement new and call it on each one of the elements in the array, why am I getting the error mentioned above?

如果您要创建一个不通过 operator new/operator new[] 的分配器,则需要显式构造元素。扩展上面的例子,你会想要一个调用 delete[] 的 destroy 方法,然后告诉 this 释放/重用内存(而不是使用 free 上面)。

如果您只是想要一个快速的解决方案,您将需要将析构函数、大小和元素计数与分配或分配器一起使用。在那种情况下,您不使用 new[]/delete[]

编辑

如果您想自己管理图书,这里有一种方法(可以有很多方向):

#include <cassert>
#include <stdio.h>
#include <new>
#include <cstdlib>

class t_allocator {
public:
t_allocator() {
}

~t_allocator() {
}

public:
/** tracks an array allocation's data. acts as a scope container for the allocation/types. */
class t_array_record {
public:
typedef void (*t_destructor)(void* const);

template<typename T>
t_array_record(T*& outObjects, t_allocator& allocator, const size_t& count) : d_mem(allocator.allocate(sizeof(T), count)), d_destructor(t_allocator::t_array_record::Destruct<T>), d_size(sizeof(T)), d_count(count), d_allocator(allocator) {
assert(this->d_mem);
/* mind exceptions */
char* const cptr(reinterpret_cast<char*>(this->d_mem));

for (size_t idx(0); idx < this->d_count; ++idx) {
/* assignment not required here: */
new (&cptr[this->d_size * idx]) T();
}

outObjects = reinterpret_cast<T*>(this->d_mem);
}

~t_array_record() {
assert(this->d_mem);
char* const cptr(reinterpret_cast<char*>(this->d_mem));

for (size_t idx(0); idx < this->d_count; ++idx) {
const size_t element(this->d_count - idx - 1U);
this->d_destructor(& cptr[this->d_size * element]);
}

this->d_allocator.free(this->d_mem);
}

private:
template<typename T>
static void Destruct(void* const ptr) {
T* const obj(reinterpret_cast<T*>(ptr));

obj->~T();
}

private:
void* const d_mem;
t_destructor d_destructor;
const size_t d_size;
const size_t d_count;
t_allocator& d_allocator;
public:
t_array_record(const t_array_record&);
t_array_record& operator=(const t_array_record&);
};
public:
void* allocate(const size_t& size, const size_t& count) {
return malloc(size * count);
}

void free(void* const mem) {
::free(mem);
}
};

演示:

class SomeClass {
public:
SomeClass() {
printf("Constructed\n");
}

virtual ~SomeClass() {
printf("Destructed\n");
}

virtual void greet() {
printf("hi: %p\n", this);
}

private:
SomeClass(const SomeClass&);
SomeClass& operator=(const SomeClass&);
};

class SomeDer : public SomeClass {
static int& N() {
static int a(0);

return ++a;
}

public:
SomeDer() : d_number(N()) {
printf("Ctor-%i\n", this->d_number);
}

virtual ~SomeDer() {
printf("~Der%i-", this->d_number);
}

virtual void greet() {
printf("Der%i-", this->d_number);
SomeClass::greet();
}

private:
const int d_number; /* << so we have different sized types in the example */
};

template<typename T>
void TryIt(const size_t& count) {
t_allocator allocator;

T* things(0);
t_allocator::t_array_record record(things, allocator, count);

for (size_t idx(0); idx < count; ++idx) {
things[idx].greet();
}
}

int main() {
TryIt<SomeClass>(3);
TryIt<SomeDer>(9);
return 0;
}

关于c++ - 如何在可能使用 placement new 的同时创建数组,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10220846/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com