言われれば当たり前だけど気付かないvectorのこと

最近Half-Edge構造のプログラムを書いていて、vectorの性質で気をつけるべきことがあることに気付いたので備忘録的に記しておきます。

Half-Edge構造のことをしっている人ならなんとなくわかると思うのですが、あるオブジェクトに何らかの別のオブジェクトを指すポインタを持たせておきたい場合があると思います。

今は分かりやすくするためにオブジェクト1とオブジェクト2と呼ぶことにしましょう。

オブジェクト1とオブジェクト2はvectorの配列に大量に格納されていて、オブジェクト1のそれぞれはいずれかのオブジェクト2を指すポインタをフィールドに持っています。

その場合に下のようなコードを書いたとします。

 vec1;
vector<obj2> vec2;
for(int i=0; i<n; i++) {
    vec1.push_back(Object1());
    vec2.push_back(Object2());
    vec1[i].obj2ptr = &vec2[i];
}

一見何の問題もないように見える(かもしれない)のですが、for文のnがとても大きな数になるとある問題が発生します。

結論から言うと、vectorが最初に確保した領域が足りなくなった時点でポインタの位置が変わってしまうのです。

ですから、読み込みが終わった後でvec1に入っているオブジェクト1からvec2に入っていると思われるオブジェクト2のポインタを参照するとアクセス違反が起こります。

正しくは、これが起こらないように最初にメモリ領域を十分に確保してやる必要があります。

 vec1;
vector<obj2> vec2;
vec1.reserve(n);
vec2.reserve(n);
for(int i=0; i<n; i++) {
    vec1.push_back(Object1());
    vec2.push_back(Object2());
    vec1[i].obj2ptr = &vec2[i];
}

こんなことをしないといけないとなると、あまり連想配列を使う意味ないんじゃないかと言う気もしてきます。

まぁオブジェクト2への参照がポインタである必然性がないのであれば配列のインデックスを持たせればいいのでしょうけど。

というわけで、今回の記事を終わります。最後までお読みいただきありがとうございました。