実体を返してハマった話
最近、ブログに書くネタが特にないほど枯れた生活を送ってましたが、久しぶりにつまらないところでハマったので、書いてみることに。
ソースのイメージは下記の通り。
class Bの中でclass Aのオブジェクトを複数保持するため、std::vectorで保持している。
#include <vector> #include <iostream> class A { public: int id; }; class B { private: std::vector<A> vec_a; public: B(); std::vector<A> get_vec_a() { return vec_a; } }; B::B() { A a; a.id = 10; vec_a.push_back(a); } int main(int argc, char** argv) { B b; for (std::vector<A>::iterator iter = b.get_vec_a().begin(); iter != b.get_vec_a().end(); iter++) { std::cout << iter->id << std::endl; } return 0; }
問題は、クライアント(main)から、class Aの要素を拾うときに、get_vec_a()というゲッターを用いて間接的にオブジェクトを取得しようとしていたところ。
ここであろうことか
std::vector<A> get_vec_a() { return vec_a; }
という形で、vec_aをわざわざコピーしてクライアントに返している。
このとき、クライアントの
for (std::vector<A>::iterator iter = b.get_vec_a().begin();
iter != b.get_vec_a().end(); iter++)
のところで、get_vec_a()を用いて、std::vectorのオブジェクトを貰っているため、vec_aのコピーが発生してしまっている。
ここまではいい。
問題は、このコピーされたvec_aの寿命がforループ内のため、スコープを外れるときにvec_aが消されてしまう。さらにvectorのデストラクタが呼ばれてしまうため、class Aの領域が破壊されてしまう。
すなわち、コピー時に下記の状態だったものが、
オリジナル ------> class Aのオブジェクト | コピー -------
vectorのデストラクタが実行された後には、
オリジナル ------> × class Aのオブジェクト | × コピー -----
となり、オリジナルからもデータが見れなくなる。もちろん、上記の簡単なサンプルであれば、デアロケータが実行された後でもデータが残っているが、この後、別のvector型を定義したら、おそらく、この領域はなくなってしまうだろう。
もちろん、解決法は実体を返さずに参照を返すこと。常識なんだろうけど、つまらないところでハマってしまった。