C ++で配列を使用する必要がありますか?


95

以来std::liststd::vector存在していますが、C ++で従来のC配列を使用する理由はありmallocますか?それとも、同じように避けるべきですか?



18
@Als:この質問は、2つの特定のコンテナーの違いに関するものですが、この質問は、生の配列と標準コンテナーの違いに関するものです。
ジョンパーディ

回答:


109

std::arrayが利用可能なC ++ 11 では、答えは「はい、配列は避けてください」です。C ++ 11より前のバージョンでは、C配列を使用して、自動ストレージ(つまり、スタック)に配列を割り当てる必要がある場合があります。


3
ただし、多くのコンパイラにはまだC ++ 11サポートがありません。それがのstd ::配列の欠如与えられた他のではなく、1を使用することをお勧めしますときに、決定する必要があります
Nowayz

4
std :: arrayはテンプレートであり、T、Nの組み合わせごとにテンプレートが新たにインスタンス化されるため、ビルド時間とコードサイズの点で大きなプロジェクトに影響を与えます。
zvrba

std :: vectorは、標準によるデータ配置を保証するため、ほとんどどこでも使用できます。C ++ 11では、C配列を使用する理由はまったくありません。
ニルス

16
@Nils配列も整列を保証します。さらに、自動ストレージ割り当て(「スタック」)は、動的ストレージ割り当てよりもはるかに高速です。正確に 3つの要素があることを知っている場合(たとえば、三角形の座標)、ベクトルを使用する理由はありません。
zvrba

9
@zvrba-std :: arrayとC配列を使用する場合は、生成されたアセンブリを確認してください。全然違いません。
Nemanja Trifunovic

85

確かに、std::arrayC ++ 11の場合でも、実際には静的データのみです。Cスタイルの配列には、次の3つの重要な利点があり std::vectorます。

  • 動的割り当ては必要ありません。このため、非常に小さな配列が多数ある場合は、Cスタイルの配列が推奨されます。n次元の点のようなものを言います。

    template <typename T, int dims>
    class Point
    {
        T myData[dims];
    // ...
    };

    通常、dimsは非常に小さく(2または3)、T組み込み型(double)であり、std::vector<Point>何百万もの要素が含まれることになると 想像できます 。あなたは間違いなく3倍の数百万の動的割り当てを望んでいません。

  • 静的初期化をサポートします。これは、次のような静的データの問題のみです。

    struct Data { int i; char const* s; };
    Data const ourData[] =
    {
        { 1, "one" },
        { 2, "two" },
        //  ...
    };

    これは、初期化のすべての順序の問題をstd::string回避するため、ベクター(および)を使用するよりも望ましい場合がよくあります。実際のコードを実行する前に、データがプリロードされます。

  • 最後に、上記に関連して、コンパイラは初期化子から配列の実際のサイズを計算できます。それらを数える必要はありません。

C ++ 11にアクセスできる場合はstd::array、最初の2つの問題を解決し、最初のケースではCスタイルの配列よりも優先して使用する必要があります。ただし、3つ目については取り上げていません。また、コンパイラーに初期化子の数に従って配列の次元を指定させることは、Cスタイルの配列を好む正当な理由です。


11
Cスタイルの配列の初期化により、自分で繰り返す必要もなくなります。int i[] = { 1, 2, 3 };での作業を続行しint i[] = { 1, 2, 3, 4 };ます。array<int, 3>をに手動で変更する必要がありますarray<int, 4>

10
@JoeWreschnig忘れがちな変更です。要素を追加するとコンパイラは文句を言うべきですが、要素を削除すると、最後に余分な0初期化要素が追加されます。この種の静的データには、引き続きCスタイルの配列を幅広く使用しています。
James Kanze

3
最初の文は意味がありません。
コンラートルドルフ

4
3番目のポイントは、etc。と同様のmake_array関数を使用することで、かなりエレガントに解決できます。@ Rのmake_pairハットチップ。マルティーニョフェルナンデス
コンラートルドルフ

@KonradRudolph:もちろんです。Andreasは「C ++で配列を使用する必要がありますか?」と尋ね、Jamesは「間違いなく、std::arrayC ++ 11では[実際に静的データに対してのみ使用する]と答えています。
ジョンパーディ

15

「決して」とは言わないでください。ただし、STLの真のデータ構造によって、その役割が大幅に減少することに同意します。

また、オブジェクト内のカプセル化により、このような選択による影響を最小限に抑えることができると私は言います。配列がプライベートデータメンバーである場合、クラスのクライアントに影響を与えることなく、配列を入れ替えたり入れたりすることができます。


11

私は、動的メモリ割り当てを使用できない安全上重要なシステムに取り組んできました。メモリは常にスタック上にある必要があります。したがって、この場合、サイズはコンパイル時に固定されるため、配列を使用します。


8
C ++ 11以前は同意していstd::array<T>ましたが、スタックに割り当てられ、基本的に生の配列にオーバーヘッドはありません。
111111 2012年

5
@ 111111-同意した。しかし、私はその業界の一部の人々がまだC ++ 11に移行していないことを知っています
Ed Heal

それが私があなたに反対票を投じなかった理由ですが、boostにはバージョンがあり、自分のロールバックも簡単だと思います。
111111 2012年

6
しかし、安全性が重要なシステムでは、新しいコンパイラー機能(テストされていない)を使用せず、ブーストを使用しません。
James Kanze、2012年

3
多くの安全上重要なシステムは、新しいコンパイラー機能さえ備えていない古いコンパイラーで構築されています。ツールチェーンの変更は、大量の事務処理、テスト、および認定を必要とする低速で高価なプロセスだからです。
ブライアンマクファーランド

6

arrayin c++は、動的サイズstd::vectorとの固定サイズ高速代替を提供しますstd::liststd :: arrayは、の追加機能の1つですc++11。Cスタイルの配列の集約型のセマンティクスを提供しながら、stdコンテナーの利点を提供します。

だからc++11私は確かstd::arrayにそれを必要とするところで、ベクトルよりも使うでしょう。しかし、私はCスタイルの配列を避けたいと思いC++03ます。


4

ほとんどの場合、いいえ、生の配列を使用する理由は考えられませんvectorsコードが新しい場合

ライブラリが配列と生のポインタを期待するコードと互換性がある必要がある場合は、配列を使用する必要があるかもしれません。


1
...しかし、C ++ 03以降、ベクトルは配列を「実際に持っている」ので、読み取りまたは書き込みのポインタでアクセスできます。そのため、配列へのポインターを期待するコードのほとんどのケースがカバーされます。ベクトルが使用できないのは、そのコードが配列を割り当てまたは解放するときだけです。
スティーブジェソップ

@SteveJessopは内部配列にアクセスできますか?
ルチアングリゴール

1
@LuchianGrigore:vector.data()C ++ 11 &vector.front()以前。
Mike Seymour

@Luchian:ベクトルが空でない場合、要素へのポインターを取得できます(空の場合は、ヌルポインターと長さ0を、大文字と小文字の区別を受け入れる任意に作成された関数に渡すことができますサイズがゼロのバッファ)。C ++ 03で追加されたベクターの隣接性保証のほぼ唯一の目的は、ポインター指向コードによってベクターをバッファーとして使用できるようにすることでした。
スティーブジェソップ

1
@SteveJessopそして、多くの人がとにかくそれが保証されていると思っていて、彼らを失望させないことが望ましいと考えられたという事実。
James Kanze、2012年

4

多くの人がスタックに配列を割り当てるためにstd :: arrayを、ヒープにstd :: vectorを指摘していることを知っています。しかし、どちらも非ネイティブアライメントをサポートしているようには見えません。SSEまたはVPX命令を使用する必要がある種類の数値コードを実行している場合(したがって、それぞれ128バイトまたは256バイトのアライメントが必要)、C配列が最善の策であると思われます。


3

静的な少量のデータを格納しているのであれば、配列は依然として有用だと思います。


2

std::vector私が考えることができる以上の配列(もちろん、必要に応じて割り当て解除を自動的に管理する何かに包まれている)の唯一の利点は、vectorコンパイラがC ++ 11をサポートしてコンストラクタを移動しない限り、そのデータの所有権を渡すことができないことです。


6
「ベクターはそのデータの所有権を渡すことができません」-はい、C ++ 03では、を使用してできswapます。
スティーブジェソップ

2

Cスタイルの配列は基本的なデータ構造であるため、使用した方がよい場合もあります。ただし、一般的なケースでは、基になるデータの隅を丸めるより高度なデータ構造を使用します。C ++を使用すると、メモリを使用して非常に興味深い便利な処理を実行できます。その多くは単純な配列で機能します。


3
Cスタイルの配列はどのようにstd::arrays よりも基本的なのですか?多くの場合、両方とも同じアセンブリにコンパイルされます。
leftaroundabout

1
より基本的であるという点でより基本的です。標準ライブラリに依存しているstd :: arrayには実装の癖があるかもしれません。
James Wynn

1
@JamesWy​​nn本当に。std::array静的配列の上に構築されたセマンティクスを正確に定義しています。
コンラートルドルフ

1

内部でSTLコンテナーを使用する必要がありますが、異なるモジュール間でそのようなコンテナーへのポインターを渡してはなりません。そうしないと、依存関係が地獄になってしまいます。例:

std::string foo;
//  fill foo with stuff
myExternalOutputProc(foo.c_str());

非常に良い解決策ですが、

std::string foo;
//  fill foo with stuff
myExternalOutputProc(&foo);

その理由は、std :: stringはさまざまな方法で実装できますが、cスタイルの文字列は常にcスタイルの文字列だからです。


標準ライブラリの異なるコンパイラ/実装を使用して作成した場合は、異なるオブジェクトコードをリンクしないでください。それは確かに本当です。これは元の質問とどのように関連していますか?
jogojapan 2013年

配列またはSTLコンテナーを使用する場合のアドバイスです。コンテナーを使用してデータを作成し、配列として渡します。文字列である他のデータについては、myExternalOutputProc(foo.rawPointerGet()、foo.count());のようなものになります。
user877329 2013年

ただし、これらの問題は、同じプロジェクトで標準ライブラリの異なる実装を組み合わせる場合にのみ発生します。それはクレイジーです。通常のコードでは、たとえば、参照によって(または、C ++ 11ではそれを移動して)ベクトルを関数に渡すことは完全に問題ありません。
jogojapan 2013年

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