c++ ゲーム 2d - なぜC ++プログラマは 'new'の使用を最小限に抑えるべきですか?





8 Answers

スタックは高速で簡単です

C ++では、与えられた関数内のすべてのローカルスコープオブジェクトに対して、スタック上にスペースを割り当てる命令が1つだけありますが、そのメモリをリークすることは不可能です。 そのコメントは、「ヒープではなくスタックを使用する」のようなものであることを意図していた(または意図していたはずです)

作り方 勉強 visual

私はstd :: list <std :: string>を使用するときにstd :: stringでスタックオーバーフローの質問memory leakを見つけました。コメントの1つはこれを示しています:

newものをあまり使わないでください。 あなたがどこにいても新しい場所を使用した理由は私には分かりません。 C ++でオブジェクトを値で作成することができます。これは言語を使用する大きなメリットの1つです。 ヒープ上のすべてを割り当てる必要はありません。 Javaプログラマのように考えるのを止めてください。

私は彼がそれによって何を意味するかは分かりません。 できるだけ頻繁にC ++でオブジェクトを作成しなければならないのはなぜですか?内部的にはどのような違いがありますか? 私は答えを誤って解釈しましたか?




newによって作成されたオブジェクトは、最終的には漏れないようにdelete必要があります。 デストラクタは呼び出されません、メモリは解放されません、ビット全体。 C ++にはガベージコレクションがないので、問題です。

値によって作成されたオブジェクト(スタック上)は、スコープから外れると自動的に終了します。 デストラクタ呼び出しはコンパイラによって挿入され、メモリは関数の復帰時に自動的に解放されます。

auto_ptrshared_ptrようなスマートポインタは、参照問題を解決しますが、規律のコーディングが必要で、他の問題(コピー可能性、参照ループなど)があります。

また、重くマルチスレッド化されたシナリオでは、 newはスレッド間の競合のポイントです。 newを過度に使用するとパフォーマンスが低下する可能性があります。 各スレッドには独自のスタックがあるため、スタックオブジェクトの作成は定義上スレッドローカルです。

値オブジェクトの欠点は、ホスト関数が返ったときに死ぬということです。値をコピーまたは返すだけで、呼び出し元に参照を渡すことはできません。




大体において、それは誰かが自分の弱みを一般的なルールに昇格させているのです。 new演算子を使用してオブジェクトを作成すること自体は間違っていません。 何らかの議論があるのは、いくつかの規律でそうする必要があるということです。オブジェクトを作成する場合は、オブジェクトが破壊されることを確認する必要があります。

これを行う最も簡単な方法は、オブジェクトを自動ストレージに作成することです。そのため、C ++はスコープ外になるとオブジェクトを破壊することを知っています。

 {
    File foo = File("foo.dat");

    // do things

 }

さて、あなたが最後の括弧の後にそのブロックから落ちるとき、 fooは範囲外であることに注意してください。 C ++は自動的にdtorを呼び出します。 Javaとは異なり、GCがそれを見つけるのを待つ必要はありません。

書かなければならなかった

 {
     File * foo = new File("foo.dat");

それを明示的に

     delete foo;
  }

File *を「スマートポインタ」として割り当てることができます。 あなたがそれに気をつけなければ、それは漏れにつながる可能性があります。

答え自体は、あなたがnewを使用しないとヒープに割り当てないという誤った仮定をします。 実際、C ++ではあなたはそれを知らない。 たった1つのポインタという小さなメモリがスタックに確実に割り当てられていることがわかっています。 しかし、Fileの実装が

  class File {
    private:
      FileImpl * fd;
    public:
      File(String fn){ fd = new FileImpl(fn);}

FileImplまだスタックに割り当てられます。

そして、はい、あなたは

     ~File(){ delete fd ; }

クラスでも。 それがなければ、 明らかにヒープに割り当てられていない場合でも、ヒープからメモリがリークします。




new()はできるだけ使用しないでください。 可能な限り注意深く使用する必要があります。 そして、それは実用主義によって指示されるように必要なだけ頻繁に使用されるべきです。

暗黙の破壊に依存するスタック上のオブジェクトの割り当ては、単純なモデルです。 オブジェクトの必要なスコープがそのモデルに合っていれば、関連delete()とNULLポインタのチェックとともにnew()を使う必要はありません。 多くの短命オブジェクトをスタックに配置する場合は、ヒープの断片化の問題を軽減する必要があります。

しかし、オブジェクトの存続期間が現在のスコープを超えて拡張する必要がある場合は、 new()が正しい答えです。 delete()とNULLポインタの可能性をいつ、どのように呼び出すか、削除されたオブジェクトと、ポインタを使用する他のすべての問題点を使用して、注意を払うようにしてください。




newを使用すると、オブジェクトがヒープに割り当てられます。 これは一般に、展開を予測するときに使用されます。 たとえば、次のようなオブジェクトを宣言すると、

Class var;

それはスタックに置かれます。

ヒープ上に置いたオブジェクトに対して常にdestroyを呼び出す必要があります。 これにより、メモリリークの可能性が開かれます。 スタック上に置かれたオブジェクトはメモリリークを起こしにくい!




私は、新しい「あまりにも多く」を使用するという考えに同意しない傾向があります。 元のポスターのシステムクラスでの新しい使用はちょっとばかげています。 ( int *i; i = new int[9999];本当にint i[9999];ははるかに明確です)コメント者のヤギを取得していたと思います。

システムオブジェクトを使って作業しているときは、まったく同じオブジェクトに複数の参照が必要なことは非常にまれです。 値が同じであれば、それはすべて重要です。 また、システムオブジェクトは通常、メモリ内の多くの領域を占有しません。 (1文字あたり1バイト、文字列内に1バイト)。 そしてもしそうならライブラリはそのメモリ管理を考慮に入れるように設計されていなければなりません(もしうまく書かれていれば)。 このような場合(コード内のニュースの1つまたは2つを除くすべて)、newは実質的に無意味であり、混乱とバグの可能性を招くだけです。

しかし、独自のクラス/オブジェクト(元のポスターのLineクラスなど)で作業する場合は、メモリのフットプリント、データの永続性などの問題について自分自身で考える必要があります。 この時点で、同じ値への複数の参照を可能にすることは非常に重要です。リンクされたリスト、辞書、グラフなどの構造が可能で、複数の変数が同じ値を持つだけでなく、 ただし、Lineクラスにはこれらの要件はありません。 元のポスターのコードは実際には全くnewものは必要ありません。




2つの理由:

  1. この場合は不要です。 あなたはコードを不必要に複雑にしています。
  2. ヒープ上に領域を割り当てます。つまり、後でそれをdeleteことを忘れないでdelete 。そうしないと、メモリリークが発生します。



new新しいものgotoです。

なぜgotoそれほど大嫌いであるかを思い出してください:それは、フロー制御のための強力で低レベルのツールですが、人々はしばしば不必要に複雑な方法でそれを使用し、コードを追跡するのを困難にしました。さらに、最も有用で最も読みやすいパターンは、構造化プログラミングステートメント(例えば、forまたはwhile)でエンコードされていました。究極の効果は、コードということであるgotoあなたが書くように誘惑されている場合は、適切な方法ですが、かなり稀であるためにgoto(あなたがいない限り、あなたはおそらく悪い事をやっている、本当に何をやっている知っています)。

new多くの場合、不必要に複雑で読みにくくするために使用され、最も有用な使用パターンはエンコードされ、さまざまなクラスにエンコードされています。さらに、すでに標準クラスがない新しい使用パターンを使用する必要がある場合は、それらをエンコードする独自のクラスを作成できます。

私もそれが主張newされるより悪いよりもgotoペアリングする必要性に起因する、newdelete声明。

同様gotonew、あなたが使用する必要があると思っているのであれば、おそらく、あなたがする必要がある動的割り当てをカプセル化することを目的としたクラスの実装以外では、




Related