45

次のような関数があるとしましょう。

int main()
{
    char* str = new char[10];

    for(int i=0;i<5;i++)
    {
        //Do stuff with str
    }

    delete[] str;
    return 0;
}
  1. strとにかくプログラムを終了する場合、なぜ削除する必要があるのでしょうか? 私が退場するつもりなら、その記憶がユニコーンでいっぱいの土地に行ってもかまいませんよね?

  2. それはただの良い習慣ですか?

  3. それはより深い結果をもたらしますか?

4

15 に答える 15

77

実際、あなたの質問が本当に「私はこの簡単なプログラムを持っています。終了する前に数バイトを解放しなくても大丈夫ですか?」答えはイエスです。最新のオペレーティング システムであれば、問題なく動作します。そして、プログラムは簡単です。これをペースメーカーに入れたり、トヨタ カムリのブレーキ システムを動かしたりするようなものではありません。唯一の顧客があなたである場合、ずさんなことによって影響を受ける可能性があるのはあなただけです。

些細なケースについて尋ねられたこの質問への回答から、自明でないケースに一般化し始めると、問題が発生します。

それでは、重要なケースについて 2 つの質問をしてみましょう。

複雑な方法でメモリの割り当てと割り当て解除を行う長時間実行サービスがあり、複数のアロケータが複数のヒープにヒットする可能性があります。通常モードでのサービスのシャットダウンは、外部状態 (ファイル、データベースなど) が一貫してシャットダウンされるようにすることを含む、複雑で時間のかかるプロセスです。シャットダウンする前に、割り当てたメモリのすべてのバイトが割り当て解除されていることを確認する必要がありますか?

はい、その理由を説明します。実行時間の長いサービスで起こりうる最悪の事態の 1 つは、誤ってメモリ リークが発生した場合です。小さな漏れでも、時間の経過とともに大きな漏れになる可能性があります。メモリ リークを見つけて修正するための標準的な手法は、アロケーション ヒープを計測して、シャットダウン時に解放されることなく割り当てられたすべてのリソースをログに記録することです。多くの誤検知を追跡し、デバッガーで多くの時間を費やすのが好きでない限り、厳密に言えば必要でなくても、常にメモリを解放してください。

ユーザーは、サービスのシャットダウンに数十億ナノ秒かかる可能性があることをすでに予想しているため、仮想アロケーターに余分な圧力をかけて、すべてがクリーンアップされていることを確認する必要がありますか? これは、大規模で複雑なソフトウェアに対して支払う対価にすぎません。また、常にサービスをシャットダウンしているわけではありません。繰り返しますが、実際よりも数ミリ秒遅くても誰が気にしますか?

私は同じ長期にわたるサービスを持っています。内部データ構造の 1 つが破損していることを検出した場合、「すばやく失敗」したいと考えています。プログラムは未定義の状態にあり、昇格された特権で実行されている可能性があります。破損した状態を検出した場合、それは私のサービスが敵対者によって活発に攻撃されているためであると想定します. 最も安全な方法は、すぐにサービスをシャットダウンすることです。サービスが稼働し続けてユーザーのデータをさらに危険にさらす危険を冒すよりも、攻撃者がクライアントへのサービスを拒否することを許可したいと思います。この緊急シャットダウンのシナリオでは、割り当てたメモリのすべてのバイトが解放されていることを確認する必要がありますか?

もちろん違います。オペレーティングシステムがそれを処理します。ヒープが破損している場合、攻撃者はエクスプロイトの一環としてメモリを解放することを望んでいる可能性があります。ミリ秒ごとにカウントされます。建物に戦術核兵器を投下する前に、わざわざドアノブを磨いたり、キッチンをモップで拭いたりする必要はありません。

それで、「プログラムが終了する前にメモリを解放する必要がありますか?」という質問に対する答えです。「それはあなたのプログラムが何をするかによって異なります」です。

于 2013-03-19T00:58:01.963 に答える
39

はい、それは良い習慣です。OS がメモリの解放を処理するとは絶対に考えないでください。この習慣に陥ると、後で失敗することになります。

ただし、質問に答えるために、メインを終了すると、OS はそのプロセスによって保持されているすべてのメモリを解放します。これには、生成した可能性のあるスレッドまたは割り当てられた変数が含まれます。OS は、他のユーザーが使用できるようにそのメモリを解放します。

于 2013-03-18T22:27:40.150 に答える
24

重要な注意:deleteメモリの解放は、ほとんど単なる副作用です。それが行う重要なことは、オブジェクトを破壊することです。RAII設計では、これは、ファイルのクローズ、OSハンドルの解放、スレッドの終了、一時ファイルの削除など、あらゆることを意味します。

これらのアクションの一部は、プロセスが終了したときにOSによって自動的に処理されますが、すべてではありません。

あなたの例では、を呼び出さない理由はありませんdelete。しかし、どちらかを呼び出す理由はないnewので、この方法で問題を回避することができます。

char str[10];

または、スマートポインターを使用して、削除(および関連する例外安全性の問題)を回避できます...

したがって、通常は、オブジェクトの存続期間が適切に管理されていることを常に確認する必要があります。

しかし、それは必ずしも簡単ではありません。静的初期化順序の大失敗の回避策は、多くの場合、OSが少数のシングルトンタイプのオブジェクトをクリーンアップすることに頼らざるを得ないことを意味します。

于 2013-03-18T22:47:31.227 に答える
16

反対の答え: いいえ、それは時間の無駄です。大量のデータが割り当てられているプログラムは、すべての割り当てを空きリストに戻すために、ほぼすべてのページにアクセスする必要があります。これにより、CPU 時間が浪費され、興味のないデータのためにメモリ プレッシャが発生し、場合によってはプロセスがディスクからページをスワップインすることさえあります。終了するだけで、それ以上の操作を行わなくても、すべてのメモリが OS に解放されます。

(「はい」の理由に同意しないわけではありません。両方の方法で議論があると思います)

于 2013-03-18T22:35:48.663 に答える
6

オペレーティング システムは、プログラムを終了するときにメモリを処理してクリーンアップする必要がありますが、一般的には、予約したメモリを解放することをお勧めします。個人的には、単純なプログラムを実行している間は、学ぶためにそうしている可能性が高いため、そうするという正しい考え方を身につけるのが最善だと思います.

いずれにせよ、メモリが確実に解放される唯一の方法は、自分で解放することです。

于 2013-03-18T22:29:52.267 に答える
4

newdelete予約キーワードの兄弟です。これらは、コード ブロックまたは親オブジェクトのライフサイクルを通じて相互に連携する必要があります。弟が過ちを犯すと(new)、兄はそれを片付けたい(delete)。そうすれば、母親(あなたのプログラム)は喜んで彼らを誇りに思うでしょう.

于 2013-03-19T07:35:58.837 に答える
3

Eric Lippert の優れたアドバイスにこれ以上同意することはできません。

それで、「プログラムが終了する前にメモリを解放する必要がありますか?」という質問に対する答えです。「それはあなたのプログラムが何をするかによって異なります」です。

ここでの他の回答は、両方の賛成論と反対論を提供していますが、問題の真の核心はあなたのプログラムが何をするかです. 動的に割り当てられる型インスタンスがカスタム クラスであり、クラス デストラクタが副作用を生成するいくつかのアクションを実行する、より重要な例を考えてみましょう。このような状況では、メモリ リークが発生するかどうかの議論は些細なことですが、より重要な問題は、そのdeleteようなクラス インスタンスの呼び出しに失敗すると、未定義の動作が発生することです。

[basic.life] 3.8 オブジェクトの有効期間
パラ 4:

プログラムは、オブジェクトが占有する記憶域を再利用するか、非自明なデストラクタを使用してクラス型のオブジェクトのデストラクタを明示的に呼び出すことにより、オブジェクトの有効期間を終了できます。自明でないデストラクタを持つクラス型のオブジェクトの場合、プログラムは、オブジェクトが占有するストレージが再利用または解放される前に、デストラクタを明示的に呼び出す必要はありません。ただし、デストラクタへの明示的な呼び出しがない場合、またはストレージを解放するために削除式 (5.3.5) が使用されていない場合、デストラクタは暗黙的に呼び出されてはならず、デストラクタによって生成される副作用に依存するすべてのプログラムは、未定義の動作があります。

したがって、あなたの質問に対する答えは、エリックが「あなたのプログラムが何をするかに依存する」と言っているようです。

于 2013-03-19T04:34:09.097 に答える
2

これは公正な質問であり、回答する際に考慮すべき点がいくつかあります。

  • 一部のオブジェクトには、削除時にメモリを解放するだけではない、より複雑なデストラクタがあります。スキップしたくない他の副作用があるかもしれません。
  • プロセスが終了したときにメモリが解放されることは、C++ 標準では保証されていません。(もちろん、最新の OS では解放されますが、それを行わない奇妙な OS を使用している場合は、メモリを適切に解放する必要があります。
  • 一方、プログラムの終了時にデストラクタを実行すると、実際にはかなりの時間がかかる可能性があり、メモリを解放するだけである場合 (いずれにせよ解放されます)、はい、単に短絡することは非常に理にかなっています代わりにすぐに終了します。
于 2013-03-20T07:28:30.163 に答える
2

とにかくプログラムを終了する場合、なぜ str を削除する必要があるのでしょうか?

怠けたくないから…

私が退場するつもりなら、その記憶がユニコーンでいっぱいの土地に行ってもかまいませんよね?

いや、ユニコーンの国もどうでもいい。アーウェンの国は別問題だ、それなら奴らの角を切り落として有効活用できる(媚薬に良いと聞いた)。

それはただの良い習慣ですか?

それは正当な良い習慣です。

それはより深い結果をもたらしますか?

他の誰かがあなたの後片付けをしなければなりません。私は何年も前に両親の屋根の下から引っ越しました。

削除せずに、コードの周りにwhile(1)ループ構造を配置します。コードの複雑さは問題ではありません。メモリ リークは、処理時間に関連しています。

デバッグの観点からは、システム リソース (ファイル ハンドルなど) を解放しないと、より重大で見つけにくいバグが発生する可能性があります。重要なメモリリークは、通常、診断がはるかに簡単です (このファイルに書き込めないのはなぜですか? )。スレッドで作業を開始すると、スタイルの悪さがさらに問題になります。

int main()
{

    while(1)
    { 
        char* str = new char[10];

        for(int i=0;i<5;i++)
        {
            //Do stuff with str
        }
    }

    delete[] str;
    return 0;
}
于 2013-03-25T16:22:31.800 に答える
2

プロの経験からの回答が多いです。ここで私はナイーブに言っていますが、私が事実として考えた答えです。

  • 概要

    3.それはより深い結果をもたらしますか?

    A:詳しくお答えします。

    2.それはただの良い習慣ですか?

    A: 良い習慣と考えられています。取得したリソース/メモリが使用されなくなったことを確認したら、それらを解放します。

    1. strとにかくプログラムを終了する場合、 なぜ削除する必要があるのでしょうか?
      私が退場するだけなら、その記憶がユニコーンでいっぱいの土地に行ってもかまいませんよね?

    A:実際、必要どうかは別として、理由を説明します。以下にいくつかの説明があります。

    依存していると思います。想定される質問をいくつか示します。プログラムという用語は、アプリケーションまたは関数のいずれかを意味する場合があります。

    Q: プログラムの機能に依存しますか?

    A:宇宙の破壊が許容できるなら、いいえ。ただし、プログラムは期待どおりに正しく動作しない可能性があり、本来の目的を完了していないプログラムでさえある可能性があります。なぜこのようなプログラムを作成するのか、真剣に考えてみませんか?

    Q: プログラムの複雑さにもよるのでしょうか?

    A: いいえ。説明を参照してください。

    Q: プログラムの安定性に依存しますか?

    A: 密接に。

    そして、私はそれが依存していると考えています

    1. プログラムの世界は?
    2. プログラムがその仕事をしたという期待はどうですか?
    3. プログラムは、他の人や、それが存在する宇宙をどの程度気にしていますか?

      ユニバースという用語については、説明を参照してください。

    要約すると、それはあなたが何気にするかによって異なります。


  • 説明

    重要:プログラムという用語を関数として定義すると、その宇宙はアプリケーションです。多くの詳細が省略されています。ただし、理解するためのアイデアとしては十分に長いです。

    アプリケーション ソフトウェアとシステム ソフトウェアの関係を示すこのような図を見たことがあるかもしれません。

    9RJKM.gif

    しかし、どちらがカバーする範囲かを認識するために、逆のレイアウトをお勧めします。ソフトウェアのみについて話しているため、次の図ではハードウェア層は省略されています。

    mjLai.jpg

    この図では、OS が最大の範囲をカバーしていることがわかります。これは、現在の宇宙であり、環境と呼ばれることもあります。全体の構造は図のようにたくさんの円盤で構成されていて、円柱かトーラス(球はいいけど想像しにくい)のどちらかだと想像できます。ここで、OS の最も外側は実際にはunibodyであり、ランタイムは実装によって単一または複数になる可能性があることに言及する必要があります。

    ランタイムが OS とアプリケーションの両方に責任を持つことは重要ですが、後者の方がより重要です。ランタイムはアプリケーションの宇宙であり、それが破壊されると、その下で実行されているすべてのアプリケーションが失われます。

    地球上の人間とは異なり、私たちはここに住んでいますが、地球で構成されていません。地球が破壊され、私たちがそこにいなかったとき、私たちはまだ他の適切な環境に住んでいます.

    しかし、私たちは宇宙に生きているだけでなく、宇宙を構成しているので、宇宙が滅びれば存在できなくなります。

    前述のように、ランタイムは OS にも責任があります。次の図の左の円は、どのように見えるかです。

    ScsZs.jpg

    これは、OS の C プログラムのようなものです。アプリケーションと OS の関係がこれと一致する場合は、上記の OS でのランタイムとまったく同じ状況です。この図では、OS はアプリケーションの宇宙です。ここでアプリケーションが OS に責任を負うべき理由は、OS がそれらのコードを仮想化しないか、クラッシュすることを許可しない可能性があるためです。OSが常にそうするのを妨げている場合、アプリケーションが何をしても、OSは自己責任です。しかし、ドライバーについて考えてみてください。この種のアプリケーションはOS の一部として扱われるため、これは OS がクラッシュすることを許可しなければならないシナリオの 1 つです。

    最後に、上の図の右側の円を見てみましょう。この場合、アプリケーション自体がユニバースになります。この種のアプリケーションをオペレーティング システムと呼ぶこともあります。OS がカスタム コードの読み込みと実行を許可していない場合、OS 自体がすべてのことを行います。それが許されても、それ自体が終了した後、メモリはハードウェア以外には行きません。必要になる可能性のあるすべての割り当て解除は、終了する前です。

    では、あなたのプログラムは他のプログラムをどの程度気にかけているでしょうか? それはその宇宙をどの程度気にしていますか?そして、プログラムがその仕事をしたという期待はどうですか? 何を気にするかによります。

于 2013-03-20T09:33:22.167 に答える
2

まだ言及されていないもう 1 つの理由は、静的および動的アナライザー ツール (valgrind や Coverity など) の出力をクリーンで静かに保つことです。メモリ リークや報告された問題がゼロのクリーンな出力は、新しい問題が発生したときに検出と修正が容易であることを意味します。

単純な例がどのように使用または進化されるかはわかりません。できるだけクリーンで鮮明に始めることをお勧めします。

于 2013-03-27T19:29:57.933 に答える
2

ほとんどのオペレーティング システムは、プロセスの終了時にメモリを解放します。例外には、特定の RTOS、古いモバイル デバイスなどが含まれる場合があります。

絶対的な意味で、アプリがメモリ リークすることはありません。ただし、実際のリークが発生しないことがわかっている場合でも、割り当てたメモリをクリーンアップすることをお勧めします。この問題は、最初からリークがない場合よりも、リークを修正するのがはるかに難しいことです。main() の機能を別の関数に移動することにしたとしましょう。あなたは本当のリークで終わるかもしれません。

それは美学にも悪いです。多くの開発者は、解放されていない「str」を見て、少し吐き気を覚えます:(

于 2013-03-20T08:17:53.010 に答える
2

言うまでもなく、C++ プログラマーの仕事に応募する場合は、delete がないために面接を通過できない可能性が非常に高いです。第一に、プログラマーは通常、リークを嫌います (そして、面接の担当者もきっとその一人でしょう)。第二に、ほとんどの企業 (少なくとも私が働いていたすべて) は「リーク禁止」ポリシーを採用しています。一般に、作成したソフトウェアは、外出先でオブジェクトを作成および破棄しながら、かなりの時間実行する必要があります。そのような環境では、漏れは災害につながる可能性があります...

于 2013-03-28T08:48:28.487 に答える
2

技術的には、プログラマーは OS に頼って何かをするべきではありません。OS は、この方法で失われたメモリを再利用する必要はありません。

動的に割り当てられたすべてのメモリを削除するコードを作成すると、コードを将来的に証明し、他の人がそれをより大きなプロジェクトで使用できるようになります。

出典: Allocation and GC Myths (PostScript アラート!)

割り当ての神話 4: ガベージ コレクションされていないプログラムは常に
割り当てたすべてのメモリの割り当てを解除します。

真実: 頻繁に実行されるコードの原因で省略された割り当て解除
漏れが増えています。それらが受け入れられることはめったにありません。しかし保持するプログラム
プログラムが終了するまでに割り当てられたほとんどのメモリ
介在する割り当て解除。Malloc は、次の場合に実装するのがはるかに簡単です。
無料はありません。

ほとんどの場合、プログラムが終了する直前にメモリの割り当てを解除すると、
無意味。とにかく、OSはそれを回収します。フリーウィルタッチアンドページイン
死んだ物体; OSはしません。

結果: 割り当てをカウントする「リーク検出器」には注意してください。
いくつかの「漏れ」は良いです!
  • free/delete を呼び出さずに malloc/new を使用するのは非常に悪い方法だと思います。

  • とにかくメモリが再利用される場合、必要なときに明示的に割り当てを解除すると、どのような害があるでしょうか?

  • おそらく、OS が free よりも速くメモリを「再利用」すると、パフォーマンスが向上します。この手法は、長時間実行し続けなければならないプログラムには役に立ちません。

そうは言っても、free/delete を使用することをお勧めします。


この習慣を身につけたら、いつの日かこのアプローチを重要な場所に誤って適用することはないと誰が言えますか?


ファイルハンドル/メモリ/ミューテックスなど、リソースの割り当てが完了したら、常にリソースの割り当てを解除する必要があります。その習慣があれば、サーバーを構築するときにそのような間違いを犯すことはありません。一部のサーバーは、24 時間 365 日稼働することが期待されています。そのような場合、何らかのリークは、サーバーが最終的にそのリソースを使い果たし、何らかの方法でハング/クラッシュすることを意味します. 短いユーティリティ プログラムです。リークはそれほど悪くありません。どんなサーバーでも、どんなリークでも死です。よろしくお願いします。自分で片付けてください。それは良い習慣です。


クラス「A」を分解する必要があると考えてください。電話しないと
「a」で「delete」すると、そのデストラクタは呼び出されません。通常、そうはなりません
とにかくプロセスが終了するかどうかは本当に重要です。しかし、デストラクタが
たとえば、データベース内のオブジェクトを解放する必要がありますか? キャッシュをログファイルにフラッシュしますか?
メモリ キャッシュをディスクに書き戻しますか? **ほら、それはただ「良い」だけではありません
オブジェクトを削除する練習をしてください。場合によっては必要です**。
于 2013-03-26T07:09:58.783 に答える