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
関数 orvector( size_type size )
コンストラクタ - 特定の値を敷き詰めたい場合は
resize
関数+fill
関数 orvector( size_type num, const TYPE &val )
コンストラクタ
とすれば良い。
##謝辞
hattorixさん、skonbさん、h2so5さん、ご指摘頂きまして本当にありがとうございます。
根本的に勘違いしていました、これをキッカケにEffective C++・STLを読み直そうかと思います。