1) new[] を使用せずにアロケータで配列を作成するにはどうすればよいですか? プレースメント付き新品ですか?もしそうなら、私が上に投稿したリンクから言及された潜在的な危険性はどうですか?
リンクの問題は、物事がどのように機能するかを誤解していることです。各実装には、割り当てられた配列に関する情報を記録する手段が定義されています。この情報は、 の実装を介してクライアントによって管理されるため、単一のオブジェクトでは必要ありません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) 配置 new を使用して、配列内の各要素に対してそれを呼び出すとしたら、なぜ上記のエラーが発生するのですか?
operator new
/を通過しないアロケーターを作成する場合は、要素を明示的に構築する必要がありますoperator new[]
。上記の例を拡張すると、を呼び出す destroy メソッドが必要になり、(上記の使用ではなく) メモリを解放/再利用するようにdelete[]
指示されます。this
free
迅速な解決策が必要な場合は、デストラクタ、サイズ、および要素数をアロケーションまたはアロケータと一緒に移動する必要があります。new[]
そのシナリオでは、 /を使用しませんdelete[]
。
編集
また、書籍を自分で管理したい場合は、次の 1 つの方法を使用できます (さまざまな方向に進む可能性があります)。
#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;
}