LoginSignup
50
36

More than 5 years have passed since last update.

C++ vector::reserveの挙動を勘違いしていた件について

Last updated at Posted at 2013-12-19

vector・stringのメンバ関数であるreserve関数の挙動を勘違いしてた。私のようによく知りもせずに使うとハマってしまうかもしれない。そもそもreserve関数で確保される容量(capacity)とは何なのか?要素数(size)とは何が違うのか?

容量(capacity) / 要素数(size)とは

  • 容量とは要素を新たに挿入しても動的なメモリ確保が行われない 許容量 のこと
  • 要素数とは実際にコンテナに格納されている要素の数のこと

容量(capacity) / 要素数(size)の注意点

  • 容量を確保するのであって要素数を大きくする訳ではない
  • イテレータendは動かずにbeginと同じ位置を指したまま
  • 容量を確保したからといって要素数以上の位置にアクセスすべきではない

また、容量が足りなくなるとvector・stringは その時点での2倍の大きさ の容量を用意してくれるので、名著Effective STLの第14項「reserve を使って不必要な割り当てを避けよう」でも示されているように下手をすれば"無駄に何度もメモリ確保関数を呼び出す遅いコード"や"無駄に長い容量を確保させて邪魔くさいコード"になってしまうので注意するべき。
ここで、以下に私が実際に書いた 書くべきでないコード(恥) を晒す。

#include<iostream>
#include<vector>
#include<algorithm>

using namespace std;
const int N = 100;
int main(void) {
  vector<int> v;
  v.reserve(N);
  fill(v.begin(), v.end() + N, 1);

  cout << "size " << v.size() << endl;
  if (v.begin() == v.end())
    for (int i = 0; i < N; i++)
      cout << v[i];
  return 0;
}

10行目でvector::end()イテレータを無理やりに書いて気持ち悪いかもしれません、大変申し訳ない
fill関数で要素数以上の位置(v.size()==0なのに添字で言えば0~N-1)にアクセスしてるのが宜しくない、fill関数を使いたいのであれば

// 省略
  v.resize(N);
  fill(v.begin(), v.end(), 1);
// 省略

と書くべき。また、別の書き方としてはコンストラクタを用いて

// 省略
  vector<int> v(N, 1);
// 省略

と書くこともできる。

つまり

事前に要素数が分かっていて

  • push_back関数などで要素を一つずつ挿入したい場合はreserve関数
  • 添字アクセスにより任意の位置に要素を代入したい場合はresize関数 or vector( size_type size )コンストラクタ
  • 特定の値を敷き詰めたい場合はresize関数+fill関数 or vector( size_type num, const TYPE &val )コンストラクタ

とすれば良い。

謝辞

hattorixさん、skonbさん、h2so5さん、ご指摘頂きまして本当にありがとうございます。
根本的に勘違いしていました、これをキッカケにEffective C++・STLを読み直そうかと思います。

50
36
4

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
50
36