実体を返してハマった話

最近、ブログに書くネタが特にないほど枯れた生活を送ってましたが、久しぶりにつまらないところでハマったので、書いてみることに。

ソースのイメージは下記の通り。

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型を定義したら、おそらく、この領域はなくなってしまうだろう。

もちろん、解決法は実体を返さずに参照を返すこと。常識なんだろうけど、つまらないところでハマってしまった。