質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.47%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

2838閲覧

vectorとarrayの使い分け

abeno2365

総合スコア25

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2021/05/26 23:42

C++において、vectorはヒープ領域へ動的に確保されるのに対して、arrayは固定長でstack領域に確保されると認識しています。
では、システムを設計する際にどのように使い分ければよいのでしょうか。
今までは先人が作った仕様などに従って使い分けてきましたが、自分で設計する場合にどちらを選べばいいかわかりません。
C言語においては、mallocで動的に確保するとメモリーの解放義務が生じる代わりに、省メモリーだと認識しています。
つまり、後からデータを追加する必要がなく、メモリーの効率をそこまで気にしない場合はArray、他はVectorということでしょうか。
よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

std::array自体をnewすればヒープ領域に保存されますし、(あまり無いと思いますが)必要であればカスタムアロケータを設定することでstd::vectorの要素をスタック領域に配置できます。

同じ数の要素をメモリ上に展開するのであれば、その分のメモリ領域は消費しますので、「省メモリー」かどうかも設計・実装次第です。std::arrayとstd::vectorの違いにはあたらないと考えます。

std::arrayとstd::vectorの違いは、「必ずN個の要素が生成される」かどうかだと思います。std::arrayでは必ずN個の要素が常に生成され存在する状態になりますし、そのためにN回のコンストラクタが一気に実行されます。なので格納するオブジェクトのコンストラクタで何か処理を行ったり、格納するオブジェクトがリソースを大きく消費する場合、それでいいのかどうかを判断する必要が出てきます。std::vectorはresize()を使えば同じようなことも実現できますし、そうでないようにも制御できます。

繰り返すと、要素数の制御を動的に行う必要があるかどうか、常にN個の要素が必要で増減しないかどうか、が使い分けのポイントではないかと思います。


5/31 コメントでの質問に対する回答

C++では、AがBを直接保持するケースでは、Aの変数が破棄されるとb_も破棄されることが保証されています(b_の破棄についてコードを書く必要はありません)。以下のケースでは、Aが破棄されたのにBが破棄されていないということは言語仕様上ありえません。

C++

1class A 2{ 3private: 4 B b_; 5};

newされたBを生ポインタで保持するケースでは、~A()にてb_を破棄する必要があります。これはなぜかというと、Aが管理しているのは、Bの実体ではなく、あくまで(Bの実体への)ポインタだからです。「AはBの実体を管理している」ということはC++文法上では何ら示されておらず、プログラマの頭の中にしかありません。このケースではdelete b_を書き忘れれば、Aが破棄されてもBが破棄されないということはありえます。

C++

1class A 2{ 3public: 4 ~A() 5 { 6 delete this->b_; 7 } 8 9 void create_b() 10 { 11 this->b_ = new B(); 12 } 13 14private: 15 B* b_; 16};

まともなC++プログラマであれば、生ポインタは使わずスマートポインタを使います。スマートポインタは、そのデストラクタで対象のオブジェクト(この場合Bの実体)のデストラクタを呼んでくれるので、~A()にて後始末のコードを記述する必要はなくなります。

C++

1class A 2{ 3public: 4 void create_b() 5 { 6 this->b_ = std::make_shared<B>(); 7 } 8 9private: 10 std::shared_ptr<B> b_; 11};

vectorは、保持している要素をきちんと破棄しています(gnuのvectorのソースコードを読むと大変勉強になると思います)。ただし以下のようにした場合は話は別です:

C++

1std::vector<B*> a; 2a.emplace_back(new B());

上記でaが保持しているのはBの実体ではなくポインタです。このようなケースではやはり自前でBの実体を破棄する必要があり、まともな(以下略)

C++

1std::vector<std::shared_ptr<B>> a; 2a.emplace_back(std::make_shared<B>());

投稿2021/05/26 23:56

編集2021/05/30 22:51
ttact

総合スコア170

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

abeno2365

2021/05/28 01:52

回答して頂きありがとうございます。 要素数が動的に変化する場合かどうかで使い分けたいと思います。
ttact

2021/05/28 02:00

私は、実装前にUML等で静的構造を設計してますが、その際に生存期間に着目して落とし込みを行っています。 オブジェクトAがオブジェクトBを複数保持する場合、AからBに集約またはコンポジションで関連を張ると思います。オブジェクトBの生存期間がオブジェクトAと同じであれば、コンポジションで張って実装はstd::array。オブジェクトBの生存期間がオブジェクトAと異なるのであれば、集約で張って実装はstd::vector。限定子を入れるならstd::map。 他に特殊な?ケースとしては、オブジェクトBのコンストラクタに渡す引数の値がオブジェクト毎に異なってループで回したい場合は、最初からN個生成するのであってもstd::vectorにするとかですかね。
abeno2365

2021/05/28 12:16

回答していただきありがとうございます。 勉強になります。 生存期間が異なって、親のAが先に消えることはありえるのでしょうか。 基本的には複数のBが自分でデコンストラクタを呼んでAより先に消えるのがほとんどなのでしょうか。 同時に消える場合は、デコンストラクタにBを消す処理を書けばいいと理解しています。
ttact

2021/05/30 22:52

生存期間に関して、回答に追記しました。
abeno2365

2021/06/01 11:07

詳細な解説ありがとうございます。 ポインタの管理責任や、スマートポインタの意義などの理解を深めることができました。 複数の質問に答えていただき本当に感謝しています。
guest

0

vector<T> : 要素Tの可変長配列
array<T,N> : 要素Tの固定長配列 (T[N]と同義)

投稿2021/05/27 00:20

episteme

総合スコア16614

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.47%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問