2つの間に重要な違いがあります。
すべてはが割り当てられていないnew
多くのC#で値型のように振る舞う(人々はしばしば、これらのオブジェクトは、おそらく最も一般的な/明白な場合で、スタック、上に割り当てられ、常にではないが、真されていると言う。より正確には、割り当てられたオブジェクト使用せずにnew
持っている自動ストレージをdurationで
割り当てられたものnew
はすべてヒープに割り当てられ、C#の参照型とまったく同じようにヒープへのポインタが返されます。
スタックに割り当てられたものはすべて、コンパイル時に決定される一定のサイズである必要があります(コンパイラーはスタックポインターを正しく設定する必要があります。または、オブジェクトが別のクラスのメンバーである場合は、他のクラスのサイズを調整する必要があります) 。そのため、C#の配列は参照型です。参照型を使用すると、実行時に必要なメモリの量を決定できるため、それらは必要です。ここでも同じことが言えます。一定のサイズ(コンパイル時に決定できるサイズ)の配列のみが、自動ストレージ期間(スタック上)で割り当てられます。動的なサイズの配列は、を呼び出してヒープに割り当てる必要がありますnew
。
(そして、ここでC#との類似性が停止します)
これで、スタックに割り当てられたものはすべて「自動」のストレージ期間になります(実際には変数をとして宣言できますがauto
、他のストレージタイプが指定されていない場合はこれがデフォルトであるため、実際にはキーワードは実際には使用されませんが、ここでから来た)
自動保存期間とは、正確に言うと、変数の期間が自動的に処理されることを意味します。対照的に、ヒープに割り当てられたものは、手動で削除する必要があります。次に例を示します。
void foo() {
bar b;
bar* b2 = new bar();
}
この関数は、検討に値する3つの値を作成します。
1行目では、スタック(自動継続時間)上のb
型の変数を宣言していますbar
。
2行目では、スタック(自動継続時間)にbar
ポインターb2
を宣言し、new を呼び出しbar
て、ヒープにオブジェクトを割り当てます。(動的期間)
関数が戻ると、次のことが起こります。最初に、b2
スコープ外になります(破棄の順序は常に構築の順序と逆です)。しかしb2
、それは単なるポインタなので、何も起こりません。それが占有するメモリは単に解放されます。そして重要なことに、それが指すメモリ(bar
ヒープ上のインスタンス)は変更されません。ポインターのみが自動継続時間を持っているため、ポインターのみが解放されます。2番目に、b
スコープ外になります。そのため、期間が自動的に設定されるため、デストラクタが呼び出され、メモリが解放されます。
そして、bar
ヒープ上のインスタンス?それはおそらくまだそこにあります。誰もそれを削除する気にならなかったので、メモリをリークしました。
この例から、自動継続時間のあるものはすべて、スコープ外になったときにデストラクタが呼び出されることが保証されていることがわかります。それは便利です。しかし、ヒープに割り当てられたものは、必要な限り存続し、配列の場合のように動的にサイズを変更できます。それも便利です。これを使用して、メモリ割り当てを管理できます。Fooクラスがそのコンストラクタのヒープにメモリを割り当て、そのメモリをデストラクタから削除した場合はどうなるでしょうか。そうすれば、すべてがスタック上にあることを強制するという制限なしに、両方の世界で最高の、再び解放されることが保証された安全なメモリ割り当てを取得できます。
そして、それはほとんどのC ++コードが動作する方法とほぼ同じです。std::vector
たとえば、標準ライブラリを見てください。これは通常、スタックに割り当てられますが、動的にサイズ変更およびサイズ変更できます。そして、必要に応じて内部的にヒープ上のメモリを割り当てることでこれを行います。クラスのユーザーにはこれが表示されないので、メモリをリークしたり、割り当てたものをクリーンアップするのを忘れたりすることはありません。
この原理はRAII(Resource Acquisition is Initialization)と呼ばれ、取得および解放する必要がある任意のリソースに拡張できます。(ネットワークソケット、ファイル、データベース接続、同期ロック)。それらはすべてコンストラクタで取得し、デストラクタで解放できるため、取得したすべてのリソースが再び解放されることが保証されます。
原則として、高レベルのコードから直接new / deleteを使用しないでください。常にメモリを管理できるクラスでラップしてください。これにより、メモリが再び解放されます。(はい、このルールには例外がある可能性があります。特に、スマートポインターはnew
、直接呼び出す必要があり、ポインターをコンストラクターに渡す必要があります。コンストラクターが引き継ぎ、delete
正しく呼び出されることを保証します。しかし、これは依然として非常に重要な経験則です)