コレクションの設計をミスったような気がする
クラスFooにはコピーコンストラクタと代入演算子が定義されている、とする。で、次のような、Fooを要素とするコレクションを定義する。
class FooCollection { private: std::vector<Foo> elements_; public: void addFoo(int x); };
addFoo() は次のような感じ。
void FooCollection::addFoo(int x) { Foo element; // いろいろ // ゴチャゴチャ elements_.push_back(element); }
addFoo() 関数のスタックフレーム内にFooオブジェクトをデフォルトコンストラクタで作って、複雑な初期化処理をしてからベクトルに追加する。ベクトルの要素はFooオブジェクトそのものだ。
上記のような状況から始まったので、FooCollectionは、その内部にFooオブジェクト達をそっくり抱え込むことになる。FooCollectionを経由しないかぎり、要素達に触ることができないので、カプセル化とか情報隠蔽の観点からも好ましいように思えた。
が、色々やっていくうちに、Foo *pFoo = new Foo(); したFooオブジェクトをFooCollectionに追加したいことが多くなった。もちろん追加はできるんだけど、pFooの先にあるオブジェクトが追加されるわけではなくて、CollectionFoo内にコピーが作られる仕様。pFooの先のオブジェクトはdeleteで消さないとマズイ(メモリーリークする)。
Fooオブジェクトは割とデカいので、newで作ってコピーして消すのはオーバーヘッドになる。FooCollection側ではFooオブジェクトへのポインターだけ持つようにすれば、コピーは避けられたわけだ。
FooとFooCollectionはもう使ってしまったいるので、タイミングと手間から直すのは難しい。
一概に「ポインター(または参照)で管理すべし」とは言えないだろうが、デカいオブジェクトをコレクション(コンテナ)内部にコピーして抱え込むのは、後々のパフォーマンスも考慮して選択すべき(だったよなーー)。
今回はディスクIOがあり、IOの重さに比べるとメモリーの確保とコピーはどうってことないので、「まーいいや」で済まされる。けど、いつでもそうだとは限らない。
- 教訓:コレクション(コンテナ)の要素は抱え込まないほうがいいかもよ。