【C++学習|初心者向け】std::shared_ptr を使いこなす!メモリ効率を劇的に上げる make_shared の活用術

導入: なぜメモリ管理を意識する必要があるのか?

C++での開発において、メモリ管理は最も頭を悩ませるポイントの一つです。std::shared_ptrは、複数の場所でオブジェクトを共有できる非常に便利なスマートポインタですが、使い方を間違えるとメモリの断片化やパフォーマンス低下を招くことがあります。「ただ使う」だけでなく、「効率的に使う」方法を知ることは、プロのエンジニアへの第一歩です。今回は、メモリ確保の回数を減らし、プログラムを高速化する重要なテクニックを解説します。

基礎知識: 制御ブロックと共有所有権

std::shared_ptrを使う際、裏側では「制御ブロック」という管理用の領域が作成されます。この制御ブロックには、現在何個のポインタがそのオブジェクトを指しているかを示す「参照カウンタ」や、破棄するためのルール(削除子)が含まれています。
通常、直接 new を使って std::shared_ptr を初期化すると、オブジェクト本体のメモリ確保と、制御ブロックのメモリ確保が別々に行われます。これはメモリ確保(アロケーション)が2回発生することを意味し、わずかですがオーバーヘッドを生んでしまいます。

実装/解決策: std::make_shared を活用しよう

この問題を解決するのが、std::make_shared 関数です。この関数を使うと、オブジェクト本体と制御ブロックを「ひと続きのメモリ領域」として一度に確保してくれます。これにより、メモリ確保の回数が1回で済むだけでなく、メモリ上でオブジェクトと制御ブロックが隣り合うため、CPUのキャッシュ効率(局所性)が向上し、プログラム全体のパフォーマンスが改善されます。

サンプルプログラム: make_shared の正しい使い方

以下のコードをコピーして、コンパイル・実行してみてください。

include
include
include

class GameCharacter {
public:
GameCharacter(std::string name) : name_(name) {
std::cout << name_ << " が生成されました。" << std::endl; } ~GameCharacter() { std::cout << name_ << " が破棄されました。" << std::endl; } private: std::string name_; }; int main() { // 【推奨】std::make_sharedを使用する方法 // オブジェクトと制御ブロックが一括で確保されます std::shared_ptr hero = std::make_shared(“勇者”);

// 別のshared_ptrで共有する
std::shared_ptr friend_hero = hero;

std::cout << "共有中の所有者数: " << hero.use_count() << std::endl; return 0; // スコープを抜けると自動的にメモリが解放されます }

応用・注意点: 現場で役立つアドバイス

std::make_shared は非常に優秀ですが、一点だけ注意が必要です。それは「オブジェクトの生存期間」です。
std::shared_ptr は、参照カウンタが0になるとオブジェクトを削除しますが、制御ブロックは「弱参照(std::weak_ptr)」が存在する限り残り続けます。std::make_shared を使うと、オブジェクト本体と制御ブロックが同じメモリ領域にあるため、弱参照が残っている間は、オブジェクト本体が解放されてもメモリ領域全体を解放することができません。
もし「非常に大きなオブジェクト」を扱っており、弱参照を多用する設計であれば、オブジェクトと制御ブロックを分けるために、あえて new を直接使用することもあります。基本は std::make_shared を使い、メモリ管理の特性を理解して使い分けましょう!

コメント

タイトルとURLをコピーしました