暴言、私見は非常に誤解を招くものであり、著者はより細かい詳細を理解しているように思われます。それは彼が誤解を招きたいように見えるだけです。私見、議論の欠陥を示す重要なポイントは次のとおりです。
void* operator new(std::size_t size, void* ptr) throw();
この標準では、上記の関数には次のプロパティがあると定義されています。
戻り値:ptr。
注:意図的に他のアクションを実行することはありません。
つまり、この関数は意図的に他のアクションを実行しません。これは、配置newが行うことの鍵であるため、非常に重要です。これは、オブジェクトのコンストラクターを呼び出すために使用され、それだけです。サイズパラメータについても言及されていないことに明示的に注意してください。
時間のない人のために、私のポイントを要約すると、「malloc」がCで行うことはすべて、「::operatornew」を使用してC++で実行できます。唯一の違いは、非集計タイプがある場合、つまり、デストラクタとコンストラクタを呼び出す必要がある型の場合は、それらのコンストラクタとデストラクタを呼び出す必要があります。このような型はCには明示的に存在しないため、「mallocの方が優れている」という引数を使用することは無効です。対応する「destroyMe」で呼び出す必要がある特別な「initializeMe」関数を持つ「C」の構造体がある場合、作成者が作成したすべてのポイントは、非集約C++構造体と同じようにその構造体に適用されます。
彼のポイントのいくつかを明示的に取り上げます:
多重継承を実装するには、コンパイラは実際にいくつかのキャスト中にポインタの値を変更する必要があります。voidに変換するときに最終的にどの値が必要かを知ることはできません*...したがって、通常の関数はC++でmallocの役割を果たすことができません。適切な戻り型はありません。
これは正しくありません。::operatornewはmallocの役割を果たします:
class A1 { };
class A2 { };
class B : public A1, public A2 { };
void foo () {
void * v = ::operator new (sizeof (B));
B * b = new (v) B(); // Placement new calls the constructor for B.
delete v;
v = ::operator new (sizeof(int));
int * i = reinterpret_cast <int*> (v);
delete v'
}
上で述べたように、Bのコンストラクターを呼び出すにはplacement newが必要です。'i'の場合、void*からint*に問題なくキャストできますが、placementnewを再度使用すると型チェックが改善されます。
彼のもう1つのポイントは、位置合わせの要件についてです。
new char [...]によって返されるメモリは、必ずしもstructintlistの配置要件を満たすとは限りません。
3.7.3.1/2の下の標準は言う:
返されるポインタは、任意の完全なオブジェクトタイプのポインタに変換され、割り当てられたストレージ内のオブジェクトまたは配列にアクセスするために使用できるように適切に配置されます(対応する割り当て解除関数の呼び出しによってストレージが明示的に割り当て解除されるまで) 。
それは私にはかなり明確に見えます。
専門のアロケータの下で、著者はあなたが抱えているかもしれない潜在的な問題を説明します。アロケータを、メモリ自体を割り当てるタイプの引数として使用する必要があります。構築されたオブジェクトでは、デストラクタを明示的に呼び出す必要があります。繰り返しますが、これは、アロケータオブジェクトをC構造体の「initalizeMe」呼び出しに渡すこととどのように異なりますか?
デストラクタの呼び出しに関しては、C ++では特別な種類のスマートポインタを簡単に作成できます。これを「placement_pointer」と呼び、スコープ外になったときにデストラクタを明示的に呼び出すように定義できます。その結果、次のことが可能になります。
template <typename T>
class placement_pointer {
// ...
~placement_pointer() {
if (*count == 0) {
m_b->~T();
}
}
// ...
T * m_b;
};
void
f ()
{
arena a;
// ...
foo *fp = new (a) foo; // must be destroyed
// ...
fp->~foo ();
placement_pointer<foo> pfp = new (a) foo; // automatically !!destructed!!
// ...
}
私がコメントしたい最後のポイントは次のとおりです。
g ++には、次のように定義された「配置」演算子new[]が付属しています。
inline void *
operator new[](size_t, void *place)
{
return place;
}
上記のように、この方法で実装するだけでなく、標準で実装する必要があります。
objをデストラクタを持つクラスとします。どこかにsizeof(obj [10])バイトのメモリがあり、その場所にobj型のオブジェクトを10個作成するとします。(C ++はsizeof(obj [10])を10 * sizeof(obj)と定義しています。)この配置演算子new []を使用してこれを行うことができますか?たとえば、次のコードはそうするように見えます。
obj *
f ()
{
void *p = special_malloc (sizeof (obj[10]));
return new (p) obj[10]; // Serious trouble...
}
残念ながら、このコードは正しくありません。一般に、演算子new []に渡されるsize_t引数が、割り当てられる配列のサイズに実際に対応するという保証はありません。
しかし、彼が定義を提供することによって強調しているように、サイズ引数は割り当て関数では使用されません。割り当て関数は何もしません。したがって、上記の配置式の唯一の影響は、予想どおりに10個の配列要素のコンストラクターを呼び出すことです。
このコードには他にも問題がありますが、作成者がリストした問題ではありません。