vectorで(ポインタではなく)オブジェクト自体を取り扱う場合、push_backしたときに次のことが行われています。
- push_backの対象となるオブジェクトをコピーコンストラクタでコピー
- 既にpush_back済みのものがあれば、再びコピーコンストラクタでそのコピーを作り、元々コピーしてあったものをデストラクタで始末する。
実際にコードを書いて確認してみました。
まずは、クラス定義から。
class Hoge { public: int num; Hoge(int num); Hoge(const Hoge& me); ~Hoge(); void printAbc(void); }; Hoge::Hoge(int aNum) { num = aNum; printf("constructer %d\n", num); } Hoge::Hoge(const Hoge& me) : num(me.num) { printf("copy constructer %d\n", num); } Hoge::~Hoge() { printf("destructer %d\n", num); }
実際にvectorを使ってクラスHogeのオブジェクトを操作してみます。
int main(int argc, char** argv) { Hoge sample1(1); Hoge sample2(2); std::vector<Hoge> iremono; iremono.push_back(sample1); iremono.push_back(sample2); return 0; }
コンパイルして実行しますと、次のような結果が得られます。
constructer 1 constructer 2 copy constructer 1 copy constructer 2 copy constructer 1 destructer 1 destructer 1 destructer 2 destructer 2 destructer 1
最初のコンストラクタ二つは、ソースの3,4行目によるものです。このコンストラクタに対するデストラクタは、最後の二つで、mainを抜ける際に、ローカル変数であるsample1,sample2に対して発生しています。
最初のコピーコンストラクタは一回目のpush_back時に、つまりsample1のpush時に発生しています。そして次のコピーコンストラクタは二回目のpush_back時、つまりsample2のpush時に発生しています。直後、一回目のコピーコンストラクタでコピーされたもののコピーが発生しています。そして、次のデストラクタで一回目のpush_back時にコピーしたもののデストラクタが発生しています。
つまり、push_back時にはpushしたものがコピーされるだけではなく、既に入っているものも同時にコピーし直されます。
そして、次のデストラクタ二つはローカル変数iremonoがmainから抜けるとき、中のオブジェクトのデストラクタが発生することを示しています。このように明示しなくても中のオブジェクトをデストラクトしてくれるのは、vectorで(ポインタではなく)オブジェクト自体を持つことのメリットだと思います。ちなみに、このときのデストラクタの発生する順番はpush_backした順番です。
私はvectorでオブジェクト自体を持つのは避けるようにしています。上記のようなメリットはあるのですが、vectorの「気遣い」であるコピーコンストラクタ、デストラクタに、反って戸惑わされる恐れがあるからです。それよりも「後始末をしっかりする」ことさえ注意していればよい「ポインタを持つ」方が楽なのでは、と考えています。