「新規」と「削除」はC ++で非推奨になっていますか?


68

さまざまなサイズの配列宣言に関するクイズに遭遇しました。最初に頭に浮かんだのは、new次のように、コマンドで動的割り当てを使用する必要があるということです。

while(T--) {
   int N;
   cin >> N;
   int *array = new int[N];
   // Do something with 'array'
   delete[] array;
}

ただし、解決策の1つが次のケースを許可することを確認しました。

while(T--) {
    int N;
    cin >> N;
    int array[N];
    // Do something with 'array'
}

少し調べた後、g ++でこれが可能であることを読みましたが、それでも、動的割り当てを使用する必要があるのはどのような場合ですか?それとも、コンパイラがこれを動的割り当てとして変換するのでしょうか?

削除機能が含まれています。ただし、ここでの質問はメモリリークに関するものではないことに注意してください。


54
2番目の例では、C ++の一部ではない可変長配列を使用しています。この場合は、std::vector代わりに(std::vector<int> array(N);)を使用してください。
一部のプログラマー、

7
あなたの質問への直接の答えは次のようになります:いいえ、非推奨になりません C ++の最新バージョンは、メモリ所有権管理(スマートポインター)を簡略化する多くの機能を提供しますが、new OBJ直接呼び出すことでオブジェクトを割り当てることは依然として一般的な方法です。
pptaszni

8
なぜメモリリークについて話しているのかについて混乱している他の人々のために、質問には重要ではないバグを修正するために質問が編集されました
Mike Caron

4
@Mannojは、動的および自動という用語をヒープとスタックに使用することを好みます。まれですが、ヒープやスタックなしでC ++を実装することは可能です。
user4581301

1
C ++で廃止されたものはなく、今後も廃止されません。これは、C ++の意味の一部です。
JoelFan

回答:


114

どちらのスニペットも慣用的な最新のC ++コードではありません。

newand delete(およびnew[]and delete[])はC ++では推奨されておらず、今後も推奨されません。これらは動的に割り当てられたオブジェクトをインスタンス化する方法です。ただし、常にa newとa delete(およびa new[]delete[])を一致させる必要があるため、これらは(ライブラリ)クラス内に保持して、これを保証します。C ++プログラマーが「新規」の使用を最小限に抑える必要がある理由を参照してください

最初のスニペットは「ネイキッド」new[]を使用しdelete[]、作成された配列を決して使用しません。それは問題だ。std::vectorここで必要なすべてをうまく実行します。何らかの形式のnew舞台裏を使用しますが(実装の詳細には触れません)、気にする必要があるのは動的配列ですが、より優れており、安全です。

2番目のスニペットは、「可変長配列」(VLA)を使用します。これは、一部のコンパイラーが拡張機能としてC ++でも使用できるC機能です。とは異なりnew、VLAは基本的にスタック(非常に限られたリソース)に割り当てられます。しかし、より重要なことに、これらは標準のC ++機能ではなく、移植性がないため、使用を避ける必要があります。動的(つまりヒープ)割り当てに取って代わるものではありません。


3
VLAは公式には規格に含まれていませんが、すべての主要なコンパイラによってサポートされているため、それらを回避するかどうかの決定は、移植性に関する現実的な懸念よりもスタイル/好みの問題のほうが多いことを付け加えておきます。
スタックトレーサー

4
また、VLAが今まで関数の実行時間よりも長生きしませんので、あなたは、配列を返すか、別の場所に保存することができないことを覚えておいてください
ルスラン

16
@StackTracer私の知る限り、MSVCはVLAをサポートしていません。そして、MSVCは間違いなく「主要なコンパイラ」です。
Max Langhof

2
「常にnewとdeleteを一致させる必要があります」を使用する場合はQt、その基本クラスにはすべてガベージコレクターがあるためnew、ほとんどの場合、それを使用して忘れます。GUI要素の場合、親ウィジェットが閉じられると、子はスコープ外になり、ガベージコレクションが自動的に行われます。
vsz

6
@vsz Qtでも、それぞれにnew一致がありdeleteます。deletesはsと同じコードブロックではなく、親ウィジェットによって実行されるだけですnew
jjramsey

22

さて、まず第一に、new/ deleteは非推奨になりません。

ただし、特定のケースでは、それらが唯一の解決策ではありません。何を選択するかは、「配列で何かを行う」コメントの下に何が隠されているかによって異なります。

2番目の例では、配列をスタックに収めようとする非標準のVLA拡張を使用しています。これには一定の制限があります。つまり、サイズが制限されており、配列が範囲外になるとこのメモリを使用できなくなります。移動することはできません。スタックがほどけると「消えます」。

したがって、ローカルの計算を行ってデータを破棄することが唯一の目標である場合、実際には問題なく動作する可能性があります。ただし、より堅牢なアプローチは、メモリを動的に割り当てることstd::vectorです。こうすることで、ランタイム値に基づいて必要なだけ正確に要素のスペースを作成できるようになります(これは、私たちがずっと行っていることです)が、それ自体もきれいにクリーンアップされ、移動できます。後で使用するためにメモリを保持したい場合は、このスコープの。

先頭に背中を旋回、vector 、おそらく使用しnew、より深いいくつかの層を、それが提示インタフェースがはるかに優れているとして、あなたは、それと心配するべきではありません。その意味で、newおよびの使用deleteは推奨されていません。


1
「...いくつかの層がより深い」ことに注意してください。独自のコンテナを実装する場合でも、newandの使用は避けdelete、のようなスマートポインタを使用する必要がありますstd::unique_pointer
最大

1
これは実際に呼び出されますstd::unique_ptr
user253751

2
@Max:std::unique_ptrのデフォルトのデストラクタ呼び出しdeleteorまたはdelete[]、つまり、所有するオブジェクトは、newまたはnew[]いずれにしても割り当てられている必要があり、その呼び出しはstd::make_uniqueC ++ 14以降に隠されています。
Laurent LA RIZZA

15

2番目の例では、可変長配列(VLA)を使用します。これは、実際にはC99(C ++ではない)機能ですが、それでもg ++でサポートされています

この回答も参照してください。

可変長配列はnew/ deleteとは異なり、決して「廃止」しないことに注意してください。

VLAはISO C ++ではないことにも注意してください。


13

最新のC ++は、動的割り当てを処理する簡単な方法を提供します。スマートポインターは、例外(許可されている場合はどこでも発生する可能性があります)後のクリーンアップと、参照されたデータ構造がスコープから外れるとすぐに早期に戻るので、代わりにこれらを使用するのが妥当です。

  int size=100;

  // This construct requires the matching delete statement.
  auto buffer_old = new int[size];

  // These versions do not require `delete`:
  std::unique_ptr<int[]> buffer_new (new int[size]);
  std::shared_ptr<int[]> buffer_new (new int[size]); 
  std::vector<int> buffer_new (size);  int* raw_access = buffer_new.data();

C ++ 14以降では、次のように書くこともできます

auto buffer_new = std::make_unique<int[]>(size);

これはさらに見栄えがよく、割り当てが失敗した場合のメモリリークを防ぎます。C ++ 20以降は、できるだけ多くのことができるはずです。

auto a = std::make_shared<int[]>(size);

私にとってこれはまだgcc 7.4.0での執筆時点ではコンパイルされません。これらの2つの例ではauto、左側の型宣言の代わりに使用します。すべての場合で、通常どおり配列を使用します。

buffer_old[0] = buffer_new[0] = 17;

doubledからのメモリリークnewとクラッシュdeleteは、C ++が長年打ちのめされてきたものであり、他の言語に切り替えるための議論の「中心」となっています。たぶん避けた方がいい。


あなたは避けなければならないunique/shared_ptrの賛成でコンストラクタをmake_unique/shared使用すると、二回(使用して構築タイプを記述する必要はありませんだけでなく、auto(あなたが失敗することができますタイプを使用している場合))が、建設が途中で失敗した場合は、メモリやリソースをリークが発生する恐れはありません
Simon Buchan

2
make_uniqueはC ++ 14の配列で使用でき、make_sharedはC ++ 20の配列でのみ使用できます。これがまだデフォルト設定であることはめったにないので、std :: make_shared <int []>(size)の提案は、少し前に私に探しました。
Audrius Meskauskas

けっこうだ!make_shared<int[]>あなたがたいていいつも欲しがるとき、私は本当にあまり使いませんvector<int>が、知っておくと良いでしょう。
Simon Buchan

過度の衒学が、IIRC unique_ptrコンストラクタはそうするために、nothrowあるTコンストラクタnothrow持っているので、液漏れの心配がありませんunique_ptr(new int[size])し、shared_ptr以下のい:削除、「例外がスローされた場合はTが配列型でない場合は、delete pが呼ばれています[ ] pそれ以外の場合。 "なので、同じ効果がありますunique/shared_ptr(new MyPossiblyAllocatingType[size])
Simon Buchan

3

newおよびdeleteは非推奨になりません。

new演算子によって作成されたオブジェクトは、参照によって渡すことができます。オブジェクトは、deleteを使用して削除できます。

newとdeleteは、言語の基本的な側面です。オブジェクトの永続性は、newおよびdeleteを使用して管理できます。これらは絶対に廃止されません。

ステートメント-int array [N]は、配列を定義する方法です。配列は、囲んでいるコードブロックのスコープ内で使用できます。オブジェクトを別の関数に渡す方法のように渡すことはできません。


2

最初の例delete[]では、最後にaが必要です。そうしないと、メモリリークが発生します。

2番目の例では、C ++でサポートされていない可変配列長を使用しています。それが唯一の配列の長さのために一定の発現を可能にします

この場合std::vector<>、ソリューションとして使用すると便利です。これは、配列に対して実行できるすべてのアクションをテンプレートクラスにラップします。


3
「until C ++ 11」とはどういう意味ですか?VLAが規格の一部になったことはないと確信しています。
チュリル

184ページのc ++ 14の標準[c ++ 14 standard](isocpp.org/files/papers/N3690.pdf)を参照してください。パラグラフ8.3.4
zig razor

4
それ標準ではありませんが、ドラフトと「ランタイムバウンドの配列」に関する部分だけが、私が知る限り、標準に含まれていませんでした。cppreferenceはVLAについて言及していません
チュリル

1
@zigrazor cppreference.comにはリストあります各標準の公開前後の最も近いドラフトへのリンクのあります。公開された標準は自由に利用できませんが、これらのドラフトは非常に近いはずです。ドキュメント番号からわかるように、リンクされたドラフトはC ++ 14の古い作業ドラフトです。
クルミ

2
@learning_dude 標準でサポートされていません。答えは(今のところ)正しいです(短いですが)。GCCが非標準の拡張機能として許可しているため、これはあなたのためだけに機能します。
クルミ

-4

構文はC ++に似ていますが、イディオムは昔ながらのAlgol60に似ています。次のようなコードブロックがあるのが一般的です。

read n;
begin
    integer array x[1:n];
    ... 
end;

例は次のように書くことができます:

while(T--) {
    int N;
    cin >> N;
    {
        int array[N];
        // Do something with 'array'
    }
}

私は時々これを現在の言語で見逃します;)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.