メモリ領域を解放する[C++11]
スマートポインタの最大のメリットは、メモリの確保と解放という煩わしさから文字通り「解放」されることです。これらにプログラマが関与しなくても済むので、くどいようですがコードの安全性も向上します。改めてになりますが、make_unique<T>関数やmake_shared<T>関数で確保されたメモリ領域は、インスタンスがスコープから抜ける際にデストラクタが自動的に呼び出されてメモリ領域が解放されます。
{
auto u_int_ptr = make_unique<int>(100);
auto s_str_ptr = make_shared<string>("hello");
// ここでメモリ領域が解放される
}
え?デストラクタっていらないんじゃないの?と思わないでくださいね。C++ですからしっかりとデストラクタは生きています。スマートポインタが、デストラクタの呼び出しを隠してくれているだけです。
新たな値が代入されたり、reset関数で新たなメモリ領域が割り当てられたときにも、それまでのメモリ領域はしっかりと解放されます。前者では「=」演算子のオーバーロードがしっかり働いているからですね。
auto u_int_ptr = make_unique<int>(100);
auto s_str_ptr = make_shared<string>("world");
// 代入で、それまで所有していたメモリ領域が解放される
u_int_ptr = make_unique<int>(200);
s_str_ptr = make_shared<string>("again");
// reset関数で、それまで所有していたメモリ領域が解放される
u_int_ptr.reset(new int(300));
s_str_ptr.reset(new string("reset"));
それでも明示的に解放したいんだよ!という場合には、reset関数を引数なしで呼び出します。明示的な解放は、スコープから抜ける前だけど、もはや不要でメモリ領域がもったいないとかそういうケースでしょうか。しかし、解放後なのにウッカリ使ってしまった!というようなコードを書けてしまうので、あまり推奨はできないでしょうね。
auto u_int_ptr = std::make_unique<int>(10);
auto s_str_ptr = std::make_shared<string>("force");
// reset関数を引数なしで呼び出すことで明示的に解放できる
u_int_ptr.reset();
s_str_ptr.reset();
まとめ
今回は、Modern C++の「ここが新しい」のうち、「生のポインタは使わないで!」で触れたスマートポインタを紹介しました。さまざまな初期化スタイルがありますが、従来どおりのポインタの延長上で安全に使えることをお伝えできたのではないかと思います。
次回は、今回もちょっとだけ登場した所有権にも関わるムーブセマンティクスを紹介します。
