このブログは、旧・はてなダイアリー「檜山正幸のキマイラ飼育記 メモ編」(http://d.hatena.ne.jp/m-hiyama-memo/)のデータを移行・保存したものであり、今後(2019年1月以降)更新の予定はありません。

今後の更新は、新しいブログ http://m-hiyama-memo.hatenablog.com/ で行います。

コレクションの設計をミスったような気がする

クラス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の重さに比べるとメモリーの確保とコピーはどうってことないので、「まーいいや」で済まされる。けど、いつでもそうだとは限らない。

  • 教訓:コレクション(コンテナ)の要素は抱え込まないほうがいいかもよ。