読者です 読者をやめる 読者になる 読者になる

クラスをSTLコンテナにいれると恐ろしい事が起こるぞ!

C++に慣れている人にとっては考えもつかないことですが、
クラスをコンテナにいれる、ということを試したくなる時期があります。
コンテナにクラスのポインタをいれるとポインタ管理が面倒だし、
クラスの実体をいれておいたら便利じゃない?
というのがその発端です。


さて、クラスはコンテナにいれてもいいものなんでしょうか?


じゃ、やってみましょう!

C++ code
- 32 lines - codepad

予想と違う結果がでたぞ!!!
と思いませんか?
なぜデストラクタが呼ばれる回数が想定より多いのでしょう?
そして、コンストラクタの呼び出される回数とデストラクタの呼び出される回数が一致していません。
むむ?
訓練されたC++はここであることに気付きます。
vectorが拡張されている時に恐ろしいことが起きているのではないか?」
では、やってみましょう。
拡張されないようにreserveです。
C++ code
- 35 lines - codepad

おお、それっぽい!!
しかし、駄目駄目ですね。
だって、コンストラクタとデストラクタが一致してないんだもん。


ここで済みません。俺は皆さんを騙していました。
なぜってC++につきものの、
「コピーコンストラクタ」と「代入演算子」がありませんね。
暗黙にこいつらが使われていると予想するのが訓練されたC++です。(嘘です
じゃ、やってみましょう。
C++ code
- 41 lines - codepad

--- 1 ---
Foo::Foo 値渡しされるクラスが生成されコンストラクタが呼ばれる
Foo::Foo(const Foo& rhs) 値渡ししたクラスがコピーコンストラクタでコピーされる
Foo::~Foo 値渡しされたクラスが破棄されるのでデストラクタが呼ばれる

ふむ。正常ですね。
では次。

--- 2 ---
Foo::Foo 値渡しされるクラスが生成されコンストラクタが呼ばれる
Foo::Foo(const Foo& rhs) 値渡ししたクラスがコピーコンストラクタでコピーされる
Foo::Foo(const Foo& rhs) ?????????
Foo::~Foo 値渡ししたクラスが破棄されるのでデストラクタが呼ばれる
Foo::~Foo ?????????

あれ、コピーコンストラクタとデストラクタが想定より多いですね?
そう、これが拡張による予期せぬコピーと破棄です。
配列を拡張するためにメモリを再確保し、コピーしているために起こる挙動です。
この挙動はやっぱりreserveをいれてやると抑制できます。


C++ code
- 41 lines - codepad

--- 1 ---
Foo::Foo
Foo::Foo(const Foo& rhs)
Foo::~Foo
--- 2 ---
Foo::Foo
Foo::Foo(const Foo& rhs)
Foo::~Foo

ふむ。
割と正常ですが、あまり芳しいとは言えません。
だって、クラスのコピーコスト高かったら嫌じゃん。


実は構造体をつくるときも、この挙動に注意しないと予期せぬコピーが発生して、
ちょっと驚いたりすることがあるので気をつけましょう。


まぁ、要するによく知らないのにコンテナに直にクラスをいれない方が良いよ、ってことです。