【C++学習|初心者向け】C++初心者必見!shared_ptrの「浅いコピー」の仕組みを正しく理解しよう

導入:なぜshared_ptrのコピーを理解する必要があるのか

C++でメモリ管理を行う際、スマートポインタであるstd::shared_ptrは非常に強力なツールです。しかし、初心者がつまずきやすいポイントの一つに「コピーの挙動」があります。shared_ptrをコピーしたとき、メモリ上のデータが二重に作られるわけではありません。この「浅いコピー」という性質を理解していないと、意図せず他の場所からデータを書き換えられてしまうなどのバグを引き起こす可能性があります。本記事では、この仕組みを正しく理解し、安全に活用する方法を解説します。

基礎知識:shared_ptrの「浅いコピー」とは?

shared_ptrにおけるコピーとは、指し示している「データそのもの」を複製するのではなく、そのデータを指す「ポインタ」と、何個のポインタがそのデータを指しているかを管理する「参照カウンタ」をコピーすることを指します。

これを専門用語で「浅いコピー(シャローコピー)」と呼びます。コピーされたすべてのshared_ptrは、メモリ上の同じ場所(同じ実体)を指し続けており、どれか一つのポインタを通じてデータを変更すると、他のすべてのポインタから見たデータも変更されます。

実装:同じデータを共有する仕組み

具体的には、コピーが作成されるたびに「参照カウンタ」が1ずつ増えます。逆に、ポインタがスコープを抜けて破棄されるとカウンタが1減ります。このカウンタが0になった瞬間、初めてメモリ上のデータが解放される仕組みです。これにより、メモリリークを自動的に防ぐことができます。

サンプルプログラム:挙動を確認する

以下のコードを実行して、コピー先で値を変更するとコピー元にも影響が出ることを確認してみてください。

include <iostream>
include <memory>
include <string>

int main() {
    // データの作成(ヒープ領域にstringを確保)
    std::shared_ptr<std::string> ptr1 = std::make_shared<std::string>("こんにちは");

    // 浅いコピーを作成(ptr1とptr2は同じメモリを指す)
    std::shared_ptr<std::string> ptr2 = ptr1;

    // データを変更
    ptr2 = "C++は楽しい!";

    // 両方のポインタからデータを確認
    std::cout << "ptr1の指す値: " << ptr1 << std::endl; // "C++は楽しい!" と表示される
    std::cout << "ptr2の指す値: " << ptr2 << std::endl; // "C++は楽しい!" と表示される

    // 参照カウンタの確認
    std::cout << "現在の参照カウンタ: " << ptr1.use_count() << std::endl; // 2 と表示される

    return 0;
}

応用・注意点:現場で陥りやすい罠

実務において注意すべき点は、「データの意図しない共有」です。

1. 副作用の注意: ある関数にshared_ptrを渡した際、その関数内でデータを書き換えると、元の場所のデータも書き換わります。これを防ぎたい場合は、データをコピーした新しいshared_ptrを作成するか、const shared_ptr<T>として受け渡しを行うことで、誤った書き換えを防止しましょう。
2. 循環参照: 互いにshared_ptrを持ち合うとカウンタがゼロにならず、メモリが解放されない「循環参照」という問題が発生します。これにはstd::weak_ptrを併用するのが鉄則です。

まずは「コピーしてもデータは一つだけ」という原則を頭に入れて、ポインタの受け渡しを行うようにしましょう。

コメント

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