[c++] なぜC ++プログラマは 'new'の使用を最小限に抑えるべきですか?



7 Answers

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

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

Question

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

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

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




大体において、それは誰かが自分の弱みを一般的なルールに昇格させているのです。 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 ; }

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




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

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

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




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

Class var;

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

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




newnewです。

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

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

私は、 new文とdelete文をペアにする必要があるため、 new文はgotoよりも悪いと主張します。

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




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

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

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

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

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




2つの理由:

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



スマートなポインタで結果をラップしても微妙なリークが発生しやすいためです。

スマートポインタでオブジェクトをラップすることを覚えている「注意深い」ユーザーを考えてみましょう。

foo(shared_ptr<T1>(new T1()), shared_ptr<T2>(new T2()));

このコードは、 shared_ptrT1またはT2 前に構築されているという保証ないため、危険です。 したがって、 new T1()またはnew T2()が失敗した後でnew T1()またはnew T2()が失敗した場合、 shared_ptrが存在しないために破棄され、割り当てが解除されないため、最初のオブジェクトはリークされます。

SolutIon:make_sharedを使用しmake_shared




Related