C ++の新しい演算子でメモリを初期化する方法は?


175

私はC ++を始めたばかりで、いくつかの良い習慣を身につけたいと思っています。私はちょうど型の配列を割り当てられている場合intnewオペレータ、どのように私はそれらをすべて自分自身をループせずに0にそれらのすべてを初期化することができますか?私は使うべきmemsetですか?それを行う「C ++」の方法はありますか?


19
良いC ++の習慣を身に付けたい場合は、配列を直接使用せずに、代わりにベクトルを使用してください。ベクターは、タイプに関係なくすべてのアイテムを初期化します。その後、delete []オペレーターを呼び出すことを覚えておく必要はありません。
brianegge 2010

@brianegge:配列を外部C関数に渡す必要がある場合、ベクトルを与えるだけでよいですか?
dreamlax 2010

12
あなたは渡すことができ&vector[0]ます。
jamesdlin 2010

もちろん、配列をC関数に渡すときは、通常、最初の要素へのポインター、@ jamesdlinが言った&vector [0]、およびこの場合はvector.size()によって提供される配列のサイズを指定する必要があります。
Trebor Rude 2014

関連(配列以外のタイプの質問):stackoverflow.com/questions/7546620/…–
アコンカグア

回答:


392

これは驚くほど知られていないC ++の機能です(まだ誰も答えを出していないという事実からも明らかです)が、実際には配列の値を初期化するための特別な構文があります。

new int[10]();

空の括弧を使用する必要があることに注意してください。たとえば、(0)その他を使用することはできません(これが値の初期化にのみ役立つ理由です)。

これは、ISO C ++ 03 5.3.4 [expr.new] / 15によって明示的に許可されています。

タイプのオブジェクトを作成するnew-expressionは、Tそのオブジェクトを次のように初期化します。

...

  • new-initializerの形式がの()場合、アイテムは値で初期化されます(8.5)。

また、これが許可されているタイプを制限しませんが、(expression-list)フォームは同じセクション内の追加のルールによって明示的に制限されているため、配列タイプは許可されません。


1
これはほとんど知られていないことに同意しますが、それが本当に非常に驚くべきことであることに(完全に)同意することはできません。これはC ++ 03で追加されました。追加しました)。
Jerry Coffin、2010

2
@ジェリー:私はまだ知らなかったことを認めなければなりません(おそらく、標準を読んだとき、それはすでにC ++ 03だったからでしょう)。そうは言っても、私が知っているすべての実装がこれをサポートしていることは注目に値します(実装するのが非常に簡単であるためだと思います)
Pavel Minaev、2010

2
はい、実装するのはかなり簡単です。新しい限り、すべての「値の初期化」はC ++ 03での新しいものでした。–
Jerry Coffin

34
C ++ 11では、均一な初期化も使用できますnew int[10] {}。次のように初期化する値を指定することもできます。new int[10] {1,2,3}
bames53

default-initializedとvalue-initializedを混同しないでください。どちらも標準で明確に定義されており、異なる初期化です。
Deduplicator 2014年

25

std :: vectorではなく配列が本当に必要だとすると、「C ++の方法」は次のようになります。

#include <algorithm> 

int* array = new int[n]; // Assuming "n" is a pre-existing variable

std::fill_n(array, n, 0); 

ただし、内部的には、これは実際には各要素を0に割り当てるループにすぎないことに注意してください(ハードウェアレベルのサポートを備えた特別なアーキテクチャを除いて、これを行う別の方法はありません)。


ループが関数の下に実装されているかどうかは問題ではありません。そのようなループを自分で実装する必要があるかどうかを知りたかっただけです。先端をありがとう。
dreamlax 2010

4
びっくりするかもしれません。だった。私のSTL(GCCとDinkumwareの両方)では、std :: copyは組み込み型で呼び出されていることを検出すると、実際にはmemcpyになります。std :: fill_nがmemsetを使用したとしても、私は驚かないでしょう。
ブライアンニール、

2
いいえ。使用「バリュー・初期化」0にすべてのメンバーを設定する
マーティンニューヨーク

24

組み込み型の配列を割り当てるメソッドはいくつかあり、これらのメソッドはすべて正しいですが、どちらを選択するかは異なります...

ループ内のすべての要素の手動初期化

int* p = new int[10];
for (int i = 0; i < 10; i++)
{
    p[i] = 0;
}

std::memsetからの関数の使用<cstring>

int* p = new int[10];
std::memset(p, 0, sizeof(int) * 10);

std::fill_nからのアルゴリズムの使用<algorithm>

int* p = new int[10];
std::fill_n(p, 10, 0);

std::vectorコンテナを使用する

std::vector<int> v(10); // elements zero'ed

もしC ++ 0xの利用可能、使用して初期化リストの機能

int a[] = { 1, 2, 3 }; // 3-element static size array
vector<int> v = { 1, 2, 3 }; // 3-element array but vector is resizeable in runtime

1
vector <int>である必要があります。p= new int [10]()を追加すると、完全なリストが得られます。
カルステン2017年

@mloskot、 "new"を使用して配列を初期化した最初のケ​​ースでは、参照渡しはどのように行われますか?int array[SIZE] ={1,2,3,4,5,6,7};表記法を使用した場合、使用void rotateArray(int (& input)[SIZE], unsigned int k);できるのは関数宣言ですが、最初の規則を使用するとどうなりますか?なにか提案を?
Anu

1
の例std::memsetが間違っていると思います-10を渡すと、バイト数を期待するようです-en.cppreference.com/w/cpp/string/byte/memsetを参照してください。(これは、なぜこのような低レベルの構成を避ける必要があるのか​​をうまく示していると思います。)
Suma

@須磨グレートキャッチ!修繕。これは10年前のバグの候補のようです:-)はい、あなたのコメントに同意します。
mloskot

7

割り当てるメモリが、何か便利なコンストラクタを持つクラスである場合、new演算子はそのコンストラクタを呼び出し、オブジェクトを初期化したままにします。

しかし、PODを割り当てる場合、オブジェクトの状態を初期化するコンストラクターを持たないまたは何かをは、メモリを割り当てて、1回の操作でnew演算子を使用してメモリを初期化することはできません。ただし、いくつかのオプションがあります。

1)代わりにスタック変数を使用します。次のように、1つのステップで割り当てとデフォルトの初期化を行うことができます

int vals[100] = {0};  // first element is a matter of style

2)を使用しますmemset()。割り当てるオブジェクトがPODではない場合は、でそれをmemsettingするのは悪い考えです。具体的な例の1つは、仮想関数を持つクラスをmemsetする場合、vtableを吹き飛ばして、オブジェクトを使用できない状態のままにします。

3)多くのオペレーティングシステムには、必要な機能を実行する呼び出しがあります。ヒープに割り当て、データを何かに初期化します。Windowsの例はVirtualAlloc()

4)これは通常、最良のオプションです。自分でメモリを管理する必要はまったくありません。STLコンテナーを使用して、すべてを一度に割り当てて初期化することを含め、生のメモリで行うこととほぼ同じことを行うことができます。

std::vector<int> myInts(100, 0);  // creates a vector of 100 ints, all set to zero

6

はいあります:

std::vector<int> vec(SIZE, 0);

動的に割り当てられた配列の代わりにベクトルを使用します。利点としては、配列を明示的に削除する(ベクターがスコープ外に出ると削除される)ことに煩わされる必要がないこと、例外がスローされた場合でもメモリが自動的に削除されることなどがあります。

編集:以下のコメントを読まない人からのドライブバイダウン投票を回避するために、この答えはベクトルが常に正しい答えであるとは限らないことをより明確にする必要があります。ただし、配列を削除することを「手動」で行うよりも、C ++の方が確実です。

現在、C ++ 11では、一定サイズの配列(vsベクトルを拡張できる)をモデル化するstd :: arrayもあります。動的に割り当てられた配列を管理するstd :: unique_ptrもあります(この質問に対する他の回答で回答されている初期化と組み合わせることができます)。これらのいずれも、配列へのポインターを手動で処理するよりもC ++の方法です。


11
これは実際に尋ねられた質問に答えるものではありません。
John Knoeller、2010

1
std::vector動的に割り当てられた配列の代わりに常に使用する必要がありますか?ベクトルではなく配列を使用する利点は何ですか?
dreamlax

1
@John Knoller:OPはC ++でそれを行う方法について尋ねました。ベクトルはこれを行うC ++の方法だと思います。もちろん、プレーンな配列を必要とする状況があり、OPの状況を知らない場合があるかもしれません。OPがベクトルについて知らないのはもっともらしいので、私はそうは思いません。
villintehaspam 2010

1
@villintehaspam:この解決策は私の質問に答えませんが、私が取ろうとしている道です。タイラー・マクヘンリーは私の質問にもっと直接答えます。特に理由が何であれ、を使用できない人にとっては役立つはずstd::vectorです。
dreamlax

2
@villintehaspam:いいえ、それはC ++の方法ではありません。それはそれを行うJavaの方法です。vectorコンテキストに関係なくどこにでも留まることを「C ++でJavaコードを書く」と呼びます。
AnT

2

std::fill片道です。2つのイテレータと1つの値を取り、領域を埋めます。それ、またはforループの方が(おそらく)C ++のほうがいいでしょう。

特にプリミティブ整数型の配列を0に設定するmemsetことは問題ありませんが、眉毛を上げる可能性があります。callocキャストのため、C ++から使用するのは少し不便ですが、も検討してください。

私の場合、ほとんど常にループを使用しています。

(私は人々の意図を推測するのは好きではありませんstd::vectorが、すべてが同等であり、を使用するよりも望ましいというのは事実ですnew[]。)


1

いつでもmemsetを使用できます。

int myArray[10];
memset( myArray, 0, 10 * sizeof( int ));

私はを使用できることを理解していますmemsetが、これが問題に取り組むためのC ++の方法であるかどうかはわかりませんでした。
dreamlax

1
これは実際には「C ++の方法」ではありませんが、どちらも生の配列ではありません。
ピートカーカム

1
@gbrandt:これは、CでもC ++でもうまく機能しないということです。タイプがcharまたはunsigned charのほとんどの値で機能します。ほとんどのタイプの値が0の場合に機能します(少なくともほとんどの実装では)。そうでなければ、それは一般的に役に立たない。
Jerry Coffin、2010

1
10 * sizeof( *myArray )よりドキュメント化されており、変更に対応してい10 * sizeof( int )ます。
Kevin Reid

1
いずれの場合でも、OPには未加工の配列があり、memsetはその配列をゼロにする最も速くて簡単な方法です。
Gregor Brandt、

-1

通常、アイテムの動的リストにはを使用しstd::vectorます。

通常、コードのその領域が将来どのように変化するかを予想して、生のメモリの動的割り当てにはmemsetまたはループを使用します。

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