web-dev-qa-db-ja.com

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

私はC++を使い始めたばかりで、良い習慣を身に付けたいと思っています。 int型の配列をnew演算子で割り当てたばかりの場合、すべてをループせずにすべてを0に初期化するにはどうすればよいですか? memsetを使用するだけですか?それを行う「C++」の方法はありますか?

159
dreamlax

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

new int[10]();

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

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

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

...

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

許可されている型は制限されませんが、(expression-list)フォームは、配列型を許可しないように、同じセクションの追加の規則によって明示的に制限されます。

355
Pavel Minaev

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

#include <algorithm> 

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

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

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

25
Tyler McHenry

組み込み型の配列を割り当てる方法は多数あり、これらの方法はすべて正しいですが、どちらを選択するかは依存します...

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

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, 10);

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

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

std::vector containerを使用

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
22
mloskot

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

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

1)代わりにスタック変数を使用します。次のように、1ステップで default-initialize を割り当てることができます。

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
7
John Dibling

はいあります:

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

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

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

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

6
villintehaspam

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

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

私の側では、ほとんど常にループを使用します。

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

2

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

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