2011-12-06 13 views
18

実行時環境では、割り当てられたブロックがデストラクタ呼び出しを必要とするオブジェクトの配列であるかどうかについての情報は保持されないため、実際にはメモリ内のどこに割り当てられているかに関する情報が保持されるため、delete[]演算子が必要であるブロックが格納されていること、およびもちろん、ブロックのサイズに依存します。
デストラクタを削除時に呼び出す必要があるかどうかを覚えておくのに、ちょっとだけのメタデータが必要です。実行時環境がプログラマの代わりに[]を削除または削除することを決定できないのはなぜですか?

私は確かに良い説明があると確信しています、私はそれを疑っていない、私はそれを知りたい。

+0

なぜ、配列の割り当て解除と破壊を処理する 'delete'の代わりに' delete [] 'があるのですか? –

+0

それはわずかな費用ですが、それを支払うことの利益も小さいです。 –

+2

割り当てのサイズがどこかに格納されていると明示していない限り(私はそれを疑う)、割り当てられたサイズを必ずしも格納する必要はありません。次の例を考えてみましょう: 'int * p = new int;'これを定義された方法で削除する唯一の方法は、 'int *'でdeleteを呼び出すことです。したがって、サイズはint型であることを暗黙のうちに知ることができます。 (注: 'void *'のdeleteの呼び出しはUBです:http://stackoverflow.com/a/941959/239916)。 –

答えて

3

ここでは、2つの事項をクリアする必要があります。

最初に:mallocは、あなたが求めた正確なサイズを保持していると仮定します。

実際はありません。 mallocは、十分に大きいブロックを提供することだけを気にします。効率上の理由からおそらく大部分を占めるわけではありませんが、おそらくおそらく "標準"サイズのブロック、例えば2^nバイトブロックを与えます。したがって、実際のサイズ(実際に割り当てられたオブジェクトの数)は事実上不明です。

セカンド:「余分なビットが」、確かにそれは、アレイの一部であるか、または単に余分なビットになるかどうかを知るために、指定されたオブジェクトに必要な情報を

を必要としていました。論理的に。

実装に関しては、実際にそのビットをどこに置くのですか?

オブジェクト自体に割り当てられたメモリには、おそらく触れないようにしてください。オブジェクトは、おそらくそれを使用しています。そう ?いくつかのプラットフォーム上

  • 、を除いて、少なくともバイト、それは余分なストレージを必要とするであろう、これは(いくつかのプラットフォームは、ビットの部分を無視する)ポインタ自体に保持することができ、これは
  • ポータブルありませんアライメントの問題では、8バイトに達する可能性があります。

デモンストレーション: (STHで述べたように説得力がありません、以下を参照)

// A plain array of doubles: 
+-------+-------+------- 
| 0 | 1 | 2 
+-------+-------+------- 

// A tentative to stash our extra bit 
+-------++-------++-------++ 
| 0 || 1 || 2 || 
+-------++-------++-------++ 

// A correction since we introduced alignment issues 
// Note: double's aligment is most probably its own size 
+-------+-------+-------+-------+-------+------- 
| 0 | bit | 1 | bit | 2 | bit 
+-------+-------+-------+-------+-------+------- 

Humpf!

EDIT

そのため、ほとんどのプラットフォーム(アドレスは重要ではない場合)には、各ポインタを「拡張」、そして実際にそれらのサイズ(アライメントの問題を)2倍にする必要があります。

余分なビットを隠すことができるように、すべてのポインタが2倍の大きさでよいですか?ほとんどの人にとって私はそうなると思う。しかし、C++は、ほとんどの人のために設計されたものではなく、速度やメモリのどちらかを問わず、パフォーマンスを気にする人のために設計されています。 EDIT OF

END

だから、正解は何ですか?正解は、タイプシステムが失った情報を回復することはコストがかかることです。残念ながら。

+1

配列の "内側"の要素へのポインタを 'delete 'するのは有効ではないので、実際にはすべての配列要素のビットを格納する必要はありません。最初の要素についてのみ、配列全体がそれに従っていないかどうかを判断する必要があります。 – sth

+0

@sth:デモンストレーションに欠陥があります...そして私は私のASCII図面に多くの愛を捧げました:(私は修正しました、通知のおかげで) –

+0

もう一度、汎用目的のヒープアロケータのオーバーヘッドを確認しましたか? –

10

私はその理由は、C++はあなたが望まないものにあなたを強制しないと思います。追加のメタデータが追加され、誰かがそれを使用しなかった場合、C++言語の設計目標とは対照的に、余分なオーバーヘッドが強制されます。

説明した機能が必要な場合は、C++ になります。それはstd::vectorと呼ばれています。ほとんどの場合、それは、newdeleteの生い茂りのものよりも、別の種類のコンテナ、またはスマートなポインタを好むべきです。

+0

恐らく... C言語の配列意味論はすでに壊れていると主張できますが、それはちょうどそこから進んでいます。私はサイズを残しておくのが時期尚早のマイクロ最適化であることを恐れています。 –

+0

私はこれが質問だったとは思わない。 –

+0

'std :: vector'について...それは動的に成長することはできません。より単純なコンテナが機能します。それにもかかわらず、十分に近い。 –

4

C++では、ブロック内の要素の数を追跡する必要がある場合は、ブロックごとに余分な4バイトを使用するだけで効率的です。

これは多くの人にとって便利なことですが、[]を気にしない人のためにも効率を上げることはできません。

これはC++とJavaの違いに似ています。ガベージコレクションについて心配する必要がないので、Javaはプログラミングがはるかに高速になる可能性がありますが、正しくプログラミングされていれば、C++はこれらの変数を格納する必要がないため、メモリブロックを削除します。

+2

ここで、汎用ヒープアロケータを確認し、各アロケーションにどれくらいのオーバーヘッドがあるかを確認します。私は各割り当てに16〜64バイトのような数字に驚くことはないので、サイズのために4〜8バイトは誰にも気付かないでしょう。そして、おそらくほとんどのアロケータは、とにかく割り当てを追跡しているでしょう。 –

4

基本的には、実装者にあまりにも多くの制限をかけたくない言語設計になります。多くのC++ランタイムは、::operator delete()の場合、とfree()(それ以下)にmalloc()を使用します。標準malloc/freeは、多くの要素の記録に必要な簿記を提供せず、free時にmallocのサイズを決定する方法を提供しません。 new Foomallocの間に、1つのオブジェクトごとに別のレベルのメモリ操作を追加すると、C/C++の観点から見ると、複雑さ/抽象度がかなり大きくなります。とりわけ、すべてのオブジェクトにこのオーバーヘッドを追加すると、オブジェクトのサイズを知るために設計されたメモリ管理アプローチが崩れることになります。

関連する問題