vector 要素オブジェクトの構築
|
|
|
|
vector<CType> を使用する上で、最初よく分からなかったのが要素オブジェクトの構築と解放のタイミングだ。
|
そこで、
|
class CTest
{
int m_foo;
public:
CTest();
CTest(const CTest&);
~CTest();
CTest &operator=(const CTest &);
};
|
|
というクラスを定義し、vector<CTest> のいろいろなメンバ関数などを呼び出して、オブジェクトの構築がどのように行われているのかを確認した。
|
|
■ vector<CTest> オブジェクト生成時
|
|
vector<CTest> vt; を実行し、ベクターオブジェクトを生成した時点では、CTest のコンストラクタ・クラスメソッドはなにもコールされなかった。
|
ベクターオブジェクトを生成しただけでは要素は何もないから、CTest のコンストラクタも呼ばれないのは当然と言えば当然である。
|
|
■ vector<CTest>::reserve()
|
|
vector<CTest>::reserve() をコールしてメモリを確保した時点では、CTest のコンストラクタ・クラスメソッドはなにもコールされなかった。
|
reserve() がベクターが領域をあらかじめ確保しておくだけなので、ベクター要素のコンストラクタはコールされない。これは当然と言えば当然だが覚えておく必要がある。
|
|
■ vector<CTest>::push_back(obj)
|
|
(1) 領域拡張が無い場合
|
reserve(1) を実行し、あらかじめ領域を確保した状態で、vector<CTest>::push_back(CTest()) を実行すると、まず CTest::CTest() がコールされた。これは push_back() の引数オブジェクトを生成するためだ。
|
次にベクターのアロケータを経由して、コピーコンストラクタ CTest::CTest(const CTest&) がコールされた。ベクター内に要素が作成されるときに呼ばれている。
|
最後に、デストラクタ CTest::~CTest() がコールされ、push_back(obj) のために生成した仮引数オブジェクトがデリートされた。
|
仮引数の処理部分を無視すれば、push_back(obj) を実行すると、コピーコンストラクタにより新しい要素が生成されることが分かった。
|
|
(2) 領域拡張がある場合
|
reserve(1) を実行し、あらかじめ領域をひとつだけ確保した状態で、vector<CTest>::push_back(aTest) を2回実行した(aTest は CTest のオブジェクト)。
|
1回目のコールではコピーコンストラクタが1回だけコールされた。要素をひとつ生成するためだ。
|
2回目のコールでは、まず、コピーコンストラクタが2回コールされた後、デストラクタが1回コールされた。
|
領域がひとつだけしか確保されていなかったので、新たに領域が確保され、コピーコンストラクタにより値がコピーされている。
|
最後に不要になった古い領域にあったオブジェクトがデリートされるときにデストラクタがコールされている。
|
push_back(obj) はコピー演算子は使用せず、コピーコンストラクタにより要素がコピーされていることがわかる。
|
これは VC++ 6.0 の実装での話なので、他の実装ではコピー演算子を使用するかもしれない。なので、コピー演算子もちゃんと定義しておいた方が安全である。
|
|
なお、push_back(obj) は insert(end(), obj) にインライン展開されているので、上記の話は insert() でも同様である。
|
|
■ vector<CTest>::resize(n)
|
|
(1) サイズを拡張する場合
|
Windows API に vector の配列を渡したい場合など、あらかじめ vector<Type>::resize(size_t) でサイズを確保したい場合がある。
生成したばかりのベクターに対し、vector<CTest>::resize(10) をコールしてみたところ、デフォルトコンストラクタが1回、コピーコンストラクタが10回、デストラクタが1回コールされた。
| ソースを見てみると、insert(end(), size, Type()); となっており、デフォルトコンストラクタでいったんテンポラリオブジェクトを生成し、それをコピーコンストラクタで各要素にコピーしていることがわかる。
| 最後に不要になったテンポラリオブジェクトを削除するときにデストラクタが呼ばれている。
|
| (2) サイズを縮小する場合
| vector<CTest>::resize(10) をコールしたあとにvector<CTest>::resize(5) をコールしてみたところ、デフォルトコンストラクタが1回、デストラクタが6回コールされた。
| デフォルトコンストラクタが呼ばれているのは resize(size_type _N, const _Ty& _X = _Ty()) と宣言されているためである。そのために削除するメンバの個数(5)+1回のデストラクタがコールされている。
|
| |