1. ホーム
  2. c++

[解決済み】なぜC++プログラマは'new'の使用を最小限に抑えなければならないのでしょうか?

2022-03-23 02:32:09

質問

私は偶然に スタックオーバーフロー 質問 std::list<std::string> を使用した場合の std::string でのメモリリーク。 , および コメント にはこう書かれています。

使用停止 new が多いですね。新しいものを使う理由がどこにもない。 ということです。で値によってオブジェクトを作成することができます。 C++ の一つで、これは この言語を使用する大きな利点です。

を割り当てる必要はありません。 ヒープ上のすべての

のような考え方はやめましょう。 ジャワ プログラマー

何を言いたいのかよくわからない。

でオブジェクトを値で作成する必要があるのはなぜか? C++ また、内部的にどのような違いがあるのでしょうか?

私が答えを誤解していたのでしょうか?

どのように解決するのですか?

広く使われているメモリ割り当て技術には、自動的な割り当てと動的な割り当てがあります。 一般的に、スタックとヒープという、それぞれに対応するメモリ領域があります。

スタック

スタックは常に順次にメモリを確保します。 これは、メモリを逆順に解放する必要があるためです(First-In, Last-Out: FILO)。 これは、多くのプログラミング言語におけるローカル変数のメモリ割り当て手法である。 最小限の簿記で済み、次に割り当てるアドレスが暗黙のうちに決まるため、非常に高速に処理できます。

C++では、これは 自動記憶 これは、スコープの終了時に自動的に保存を要求するためである。 現在のコードブロックの実行が開始されるとすぐに、( {} が完了すると、そのブロック内のすべての変数のメモリが自動的に回収されます。 また、この瞬間に デストラクタ が呼び出され、リソースの後始末が行われます。

ヒープ

ヒープを使用すると、より柔軟なメモリ割り当てモードが可能になります。 記帳はより複雑になり、割り当てはより遅くなります。 暗黙の解放ポイントがないため、メモリを手動で解放する必要があります。 delete または delete[] ( free C言語の場合)。 しかし、暗黙の解放点がないことが、ヒープの柔軟性の秘訣です。

ダイナミックアロケーションを使用する理由

ヒープを使用すると遅くなり、メモリリークやメモリの断片化を引き起こす可能性があるとしても、動的割り当ての方が制限が少ないので、完全に良い使用例があります。

ダイナミックアロケーションを使用する2つの主な理由。

  • コンパイル時に必要なメモリ量がわからない。 例えば、テキストファイルを文字列に読み込む場合、通常、ファイルのサイズが分からないので、プログラムを実行するまで、どれだけのメモリを確保すればよいのか判断できない。

  • 現在のブロックを離れた後も持続するようなメモリを確保したい。 たとえば、次のような関数を書きたい。 string readfile(string path) は、ファイルの内容を返すものです。 この場合、たとえスタックがファイルの内容全体を保持できたとしても、関数から戻って、割り当てられたメモリブロックを保持することはできません。

ダイナミックアロケーションが不要になりがちな理由

C++には デストラクタ . この仕組みにより、リソースの寿命と変数の寿命を一致させることで、リソースを管理することができます。この技法は RAII C++の特徴である。これは、リソースをオブジェクトにラップするものです。 std::string がその典型例です。 このスニペットは

int main ( int argc, char* argv[] )
{
    std::string program(argv[0]);
}

は、実際には可変量のメモリを割り当てます。 そのため std::string オブジェクトはヒープを使用してメモリを確保し、デストラクタでそれを解放します。 この場合、あなたが行ったのは ない は、手動でリソースを管理する必要がなく、動的メモリ割り当ての利点を享受できます。

特に、このスニペットでは、そのことを暗示しています。

int main ( int argc, char* argv[] )
{
    std::string * program = new std::string(argv[0]);  // Bad!
    delete program;
}


不要な動的メモリ割り当てがあります。 このプログラムは、より多くのタイピングを必要とし(!)、メモリの割り当て解除を忘れる危険性があります。 このようなことをしても、明らかな利点はありません。

自動保存をできるだけ頻繁に使用すべき理由

基本的には、最後の段落に集約されています。 自動保存をできるだけ頻繁に使用することで、プログラムを作ることができます。

  • 入力が速くなる。
  • 実行時の速度が速い
  • メモリやリソースのリークが少ない。

ボーナスポイント

参照された質問では、さらなる懸念事項があります。 特に、以下のクラスです。

class Line {
public:
    Line();
    ~Line();
    std::string* mString;
};

Line::Line() {
    mString = new std::string("foo_bar");
}

Line::~Line() {
    delete mString;
}

は、実は以下のものよりも、使うのにリスクが高いのです。

class Line {
public:
    Line();
    std::string mString;
};

Line::Line() {
    mString = "foo_bar";
    // note: there is a cleaner way to write this.
}

その理由は std::string はコピーコンストラクタを適切に定義しています。 次のようなプログラムを考えてみましょう。

int main ()
{
    Line l1;
    Line l2 = l1;
}

オリジナル版を使用すると、このプログラムはクラッシュする可能性が高いです。 delete を同じ文字列で2回表示します。 修正版を使用すると、各 Line インスタンスは自身の文字列を所有する インスタンス それぞれは独自のメモリを持ち、プログラムの終了時に両方とも解放されます。

その他の注意事項

を多用する。 RAII がC++のベストプラクティスとされているのは、上記のような理由からです。 しかし、すぐには明らかにならないが、さらなる利点がある。 基本的に、それは部分の総和よりも優れているのです。 機構全体が コンポーザー . スケールするのです。

を使用すると Line クラスをビルディングブロックとして使用します。

 class Table
 {
      Line borders[4];
 };

次に

 int main ()
 {
     Table table;
 }

が4つ割り当てられています。 std::string インスタンス、4つの Line インスタンス、1 Table インスタンスとすべての文字列の内容、そして はすべて自動的に解放されます。 .