std::vectorにはreserveというメソッドがあるがどのように動作するのか調べてみた。
struct Info {
uint8_t* dex = nullptr;
Info(){
dex = new uint8_t;
*dex = 7;
printf("%p Info() dex %p %d\n", this, dex, *dex);
}
Info(const Info& obj){
dex = new uint8_t;
*dex = *obj.dex;
printf("%p Info(const Info&) dex %p %d\n", this, dex, *dex);
}
#if 0
#endif
~Info(){
if (dex != nullptr){
printf("%p ~Info() dex %p %d\n", this, dex, *dex);
} else {
printf("%p ~Info() dex %p\n", this, dex);
}
delete dex;
}
};
int main(int argc, const char * argv[]) {
std::vector<Info> v;
v.reserve(2);
printf("v.data() %p\n", v.data());
v.push_back(Info());
printf("v[0] %p dex %p %d\n", &v.at(0), v.at(0).dex, *(v.at(0).dex));
while(true){
}
return 0;
}
v.data() 0x100200170
0x7fff5fbff6b0 Info() dex 0x100103b50 7
0x100200170 Info(const Info&) dex 0x100103b60 7
0x7fff5fbff6b0 ~Info() dex 0x100103b50 7
v[0] 0x100200170 dex 0x100103b60 7
reserveによってアロケートされるのは構造体のメンバ変数のみつまり、uint8_t* dex のポインタサイズ分だけである。
1回目のコンストラクタはpush_backの中のInfo()によるものである。
2回目のコンストラクタはpush_backの中で生成されたInfo()をコピーコンストラクタで
要素の0番目のアドレスの位置にコピーしているところである。
ここでのコンストラクタのアドレスはv_data()のアドレスと同じであることから、メモリのアロケートは
行われずにコンストラクタだけが実行されている。
new expression - cppreference.com
これはここにあるように、placement newと呼ばれるものである。
以下のように既にアロケートされたメモリ領域にTを埋め込み new(ptr) T と呼び出すことで、コンストラクタを実行し、
T*のポインタを得ている。
char* ptr = new char[sizeof(T)];
T* tptr = new(ptr) T;
tptr->~T();
delete[] ptr;
さて、ここでついでに#if 0の部分をコメントアウトし、ムーブコンストラクタを定義して再度実行してみよう。
v.data() 0x100103b30
0x7fff5fbff6b0 Info() dex 0x100103b40 7
0x100103b30 Info(Info&&)
0x7fff5fbff6b0 ~Info() dex 0x0
v[0] 0x100103b30 dex 0x100103b40 7
push_backの引数部分は右辺値であるからムーブコンストラクタが呼び出されているのが分かる。
ムーブコンストラクタが未定義の場合、コピーコンストラクタが呼び出されるようだ。