9

Stroustrup は、彼の新しい本TC++PL4 で、ユーザー制御のメモリ割り当てと配置、より具体的には、謎めいた「配置」に関して、かつては一般的だった慣行 に少し異なる光を当てています。本の宗派で。11.2.4、Stroustrup は次のように書いています。newdelete

「配置delete」演算子は、削除されたポインターが安全に派生されなくなったことをガベージコレクターに通知する可能性があることを除いて、何もしません。

これは、健全なプログラミングの実践が、配置への呼び出しdeleteによるデストラクタへの明示的な呼び出しに続くことを意味します。

けっこうだ。deleteただし、あいまいなものよりも配置を呼び出すためのより良い構文はありませんか

::operator delete(p);

私が尋ねる理由は、ストラウストラップの宗派です。11.2.4 では、そのような奇妙な構文は言及されていません。実際、Stroustrup はこの問題について深く考えていません。彼は構文についてまったく言及していません。::operator名前空間の解決の問題を、特に名前空間とはまったく関係のないものに割り込ませているの外観が、漠然と嫌いです。これ以上洗練された構文はありませんか?

参考までに、Stroustrup のより完全な文脈での引用を以下に示します。

デフォルトでは、オペレータnewはそのオブジェクトをフリー ストアに作成します。オブジェクトを別の場所に割り当てたい場合はどうすればよいでしょうか?...アロケータ関数に追加の引数を指定し、使用時にそのような追加の引数を指定することで、オブジェクトを任意の場所に配置できますnew

void* operator new(size_t, void* p) { return p; }

void buf = reinterpret_cast<void*>(0xF00F);
X* p2 = new(buf) X;

この使用法により、new(buf) X追加の引数を に提供するための構文は、配置構文operator new()として知られています。everyは最初の引数としてサイズを取り、割り当てられたオブジェクトのサイズは暗黙的に提供される ことに注意してください。演算子によって使用される は、通常の引数一致規則によって選択されます。everyの最初の引数は aです。operator new()operator new()newoperator new()size_t

「配置」operator new()は、そのような最も単純なアロケーターです。これは、標準ヘッダーで定義されています<new>

void* operator new (size_t, void* p) noexcept;
void* operator new[](size_t, void* p) noexcept;

void* operator delete (void* p, void*) noexcept; // if (p) make *p invalid
void* operator delete[](void* p, void*) noexcept;

「配置delete」演算子は、削除されたポインターが安全に派生されなくなったことをガベージコレクターに通知する可能性があることを除いて、何もしません。

Stroustrup はその後new、アリーナでの配置の使用について議論を続けています。delete彼は再び配置について言及していないようです。

4

3 に答える 3

2

これは、健全なプログラミングの実践は、デストラクタへの明示的な呼び出しの後に配置削除の呼び出しを行うことを意味します。

いいえ、そうではありません。IIUC Stroustrup は、メモリが使用されなくなったことをガベージ コレクターに通知するために配置delete必要であることを意味するのではなく、それ以外は何もしないことを意味します。すべての割り当て解除関数は、ガベージ コレクターのメモリが使用されなくなったことを通知できますが、配置を使用してnew自分でメモリを管理する場合、ガベージ コレクターにそのメモリをいじらせる必要があるでしょうか?

::operator名前空間の解決の問題を、特に名前空間とはまったく関係のないものに割り込ませているの外観が、私は漠然と嫌いです。

「適切に」それ名前空間と関係があり、「グローバル」を参照するように修飾することで、クラス型operator newのオーバーロードと区別されます。operator new

これ以上洗練された構文はありませんか?

あなたはおそらくそれを呼びたくないでしょう。配置を使用し、コンストラクターが例外をスローすると、配置演算子がコンパイラーによってdelete呼び出されます。割り当てを解除するメモリがないため (ペースメントが割り当てていないため)、メモリが未使用としてマークされる可能性があります。newnew

于 2013-07-03T10:59:45.837 に答える
2

を使用したくない場合::は、実際に使用する必要はありません。実際、一般的にはすべきではありません (したくありません)。

::operator newand ::operator delete(および配列バリアントは使用しないでください) の置換を提供できます。

ただし、クラスに対してoperator newandoperator deleteをオーバーロードすることもできます (また、配列バリアントを実行できますが、使用しないでください)。

のようなものを使用すると、割り当てがクラス固有のものを使用する代わりにvoid *x = ::operator new(some_size);グローバルに直接移動するように強制されます(存在する場合)。operator newもちろん、一般的に、クラス固有のものがある場合はそれを使用します (存在しない場合はグローバルなものを使用します)。それはまさにあなたが使用することから得られるものですvoid *x = operator new(some_size);(つまり、スコープ解決演算子なし)。

いつものように、 news とdeletes が一致していることを確認する必要があるため、メモリを割り当てていた::operator delete場合にのみメモリを削除する必要があります。::operator newほとんどの場合::、どちらでも使用しないでください。

その主な例外は、あるクラスのoperator newandを実際に書いているとき/場合です。operator deleteこれらは通常::operator new、メモリの大きなチャンクを取得するために呼び出し、それをオブジェクト サイズの断片に分割します。その大きなメモリ チャンクを割り当てるには、通常 (常に?) 明示的に指定する必要があります::operator new。明らかに、::operator newいつデータを割り当てるかを指定する場合は、一致するように指定する必要もあります::operator delete

于 2013-06-30T17:04:37.033 に答える