make_shared の弱点
make_shared で確保されたメモリ領域は,それを参照する weak_ptr が無くならない限り解放されない
がイマイチしっくり来なかったので、自分でコード作る+make_shared
のコードを追いかけて理解したので、
それのメモ。
以下のようなコードをメモリ使用量を監視しながらステップ実行して確かにそのようなことを起こっていることを確認出来た。
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <array> | |
#include <iostream> | |
#include <memory> | |
using namespace std; | |
struct BigData { | |
std::array<char, 100000000> data; | |
}; | |
int main(void){ | |
{ | |
std::weak_ptr<BigData> weak; | |
{ | |
auto num = std::make_shared<BigData>(); | |
weak = std::weak_ptr<BigData>(num); | |
double sum = 0; | |
for (int i = 0; i < 100000000; ++i) sum += num->datai; | |
cout << "Create weak pointer." << endl; | |
cout << "Sum: " << sum << endl; | |
} | |
cout << "Weak pointer size: " << sizeof(weak) << "MB" << endl; | |
} | |
cout << "Weak pointer destroyed." << endl; | |
return 0; | |
} |
というわけで、どうしてそうなっているのかをソース追いかけてみる。 そうすると内部で、
_Ref_count_obj<_Ty> *_Rx =
new _Ref_count_obj<_Ty>(_STD forward<_Types>(_Args)...);
と内部オブジェクトを作っているようだが、事実上これが shared_ptr
の中身だと思って良さそうだ。
で、実際にそのコンストラクタのコードがこんな感じ。
template<class... _Types>
_Ref_count_obj(_Types&&... _Args)
: _Ref_count_base()
{ // construct from argument list
::new ((void *)&_Storage) _Ty(_STD forward<_Types>(_Args)...);
}
つまり、placement new を使って _Ref_count_obj
の中に、そのままオブジェクトを構築していることになる。
この _Ref_count_obj
は _Ref_count_base
を継承しているが、これが参照カウンタを持っている。
管理用のカウンタとオブジェクト本体が一緒になっているので、
オブジェクトを参照している weak_ptr
と shared_ptr
がなくならなければオブジェクトが開放されないというのも納得である。
make_shared
の中ではメモリの確保回数を減らすためにこのような処理を行っている、ということがわかっていればそれほど難しい事ではなかった。