我一直在努力创建一个自定义分配器作为一个有趣的练习/练习,我跑了与创建阵列两个电位的问题。 对于一个典型的呼叫用于分配,我将使用malloc
和placement new
。 然而,当我去创建一个数组,我就应该怎么做困惑。 这一次,我在,它似乎有些地方注意到placement new
可能不是数组安全,如这里 。 我也遇到了自己的错误,而试图使用placement new
的数组。 我会得到的错误
`错误C2679:二进制“=”:没有操作员发现这需要类型的右边的操作数“SomeClass的*”(或没有可接受的转化率)
我理解的错误(我认为),但我宁愿错误通过我的数组构造方法来解决。 我有两个问题
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)一个分配器如何创建,而无需使用新的[]数组? 它是与安置的新? 如果是这样,怎么样从我上面贴的链接中提到的潜在危险?
在链接的问题是误解是如何工作的。 各实施方案具有一个实现中定义的装置来记录所分配的阵列信息。 此信息不需要用单个对象,因为它是由客户端通过实施管理delete
。
对于数组,执行必须记录的事情,如元素计数,析构函数调用(如适用),元素大小......这些东西往往是存储在返回分配的开始,和实现明显适当地抵消了分配请求的大小。 因此,实际的大小,以便容纳这些隐藏的值偏移。 这就是为什么malloc(sizeof...)
不会工作,除非你不分配额外的簿记(其中,顺便说一句, std::allocator
和集合接口带来的)。
为了正确地记录这些信息,你可以定义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[]
如果你的分配器可能会抛出。
如果你希望你的分配做一些记录时,它就会变得混乱。 就个人而言,我不认为这种情况是由语言实现好,特别是因为数组初始化并没有落实到位。 总是会有执行近施工或破坏,或在这种情况下使用一些全局访问数据的额外步骤。
2)如果我想使用新的布局,并调用它的数组中的元素中的每一个,为什么我收到上面提到的错误?
你需要显式地构造元素,如果你正在创建一个allocator不通过走operator new
/ operator new[]
扩展上面的例子中,你会想破坏方法,要求delete[]
然后告诉this
自由/重用内存(而不是使用free
以上)。
如果你只是想快速解决方案,无线本地环路需要挑着析构函数,大小和元素计数与周围的分配或分配。 在scenarion,您不使用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;
}
mem[i]
的类型为Type&
而new(mem + i) Type();
有型Type*
。 这些都是明显不符的类型,不能进行分配。 我相信你可以完全删除分配,它会工作了,仍然在初始化你该位置的记忆。
我还是稍微谨慎实施,虽然自己的阵列分配子(自定义分配器vector
将是例如更明显)。