C++のポインタとスマートポインタの使い方を徹底解説!

c++
スポンサーリンク

はじめに

C++のポインタは、メモリ管理を理解し、効率的なプログラムを書く上で非常に重要な概念です。本記事では、ポインタの基本からスマートポインタまでを詳しく解説し、実践的なサンプルコードを通じて理解を深めます。

メモリとアドレスの基礎

メモリとは?

メモリは、プログラムが実行される際にデータを保存する領域です。メモリは巨大な配列のようなもので、各要素には一意のアドレス(メモリの位置)が割り当てられます。

変数とメモリの関係

C++では、変数を宣言すると、その変数に対応するメモリ領域が確保されます。例えば、

int a = 10;

このとき、変数 a に対応するメモリ領域が確保され、そのアドレスを調べるには & 演算子を使用します。

cout << &a << endl; // 変数aのアドレスを表示

sizeof 演算子で型のサイズを確認

メモリ上で各データ型がどれくらいのサイズを持つかを sizeof 演算子で調べることができます。

cout << sizeof(int32_t) << endl; // 4
cout << sizeof(int8_t) << endl;  // 1

これは、整数型 int32_t が 4 バイト、int8_t が 1 バイトであることを示しています。

ポインタとは?

ポインタの基本概念

ポインタとは、変数のメモリアドレスを保持する特殊な変数です。ポインタ型の変数を使うことで、メモリ操作を直接行うことができます。

ポインタの宣言と使用方法

int a = 42;
int *p = &a; // 変数aのアドレスをポインタpに格納
cout << *p << endl; // ポインタを使ってaの値を取得(42)
  • int *p;pint 型のデータを指すポインタ。
  • p = &a;p に変数 a のアドレスを格納。
  • *pp が指す変数 a の値を取得。
  • メモリ領域の種類

    C++のメモリは、以下の3つの領域に分かれます。

    静的領域(Static Area)

  • グローバル変数や静的変数が保存される領域。
  • プログラム開始時に確保され、終了時に解放。
  • スタック領域(Stack Area)

  • ローカル変数や関数の引数が保存される領域。
  • 関数の呼び出しとともに確保され、関数終了時に自動解放。
  • ヒープ領域(Heap Area)

  • new 演算子で動的にメモリを確保。
  • delete で手動で解放が必要。
  • ヒープ領域の確保と解放

    ヒープ領域の確保

    int *p = new int; // 1つのint型の領域を確保
    *p = 100;
    cout << *p << endl; // 100

    ヒープ領域の解放

    delete p; // 確保したメモリを解放

    配列の確保と解放

    int *arr = new int[10]; // 10個分のメモリ確保
    for (int i = 0; i < 10; i++) arr[i] = i;
    delete[] arr; // 配列用のdelete[]

    スマートポインタとは?

    手動の delete は危険!

    手動で delete を忘れるとメモリリークが発生し、プログラムの動作が不安定になります。

    スマートポインタのメリット

    C++11以降では、std::unique_ptrstd::shared_ptr を使うことで、delete の必要がなくなり、安全にメモリ管理ができます。

    std::unique_ptr の使い方

    基本の使用方法

    #include <memory>
    int main() {
        std::unique_ptr<int> p1 = std::make_unique<int>(123);
        cout << *p1 << endl; // 123
    }
  • std::make_unique<int>(123);int 型の領域を確保し、123で初期化。
  • p1 がスコープを抜けると、自動的にメモリ解放。
  • 所有権の移動(move)

    std::unique_ptr<int> p2;
    p2 = std::move(p1); // p1 から p2 に所有権を移動

    std::shared_ptr の使い方

    複数のポインタで共有

    #include <memory>
    int main() {
        std::shared_ptr<int> p1 = std::make_shared<int>(123);
        std::shared_ptr<int> p2 = p1; // 共有
        cout << *p1 << endl; // 123
        cout << *p2 << endl; // 123
    }
  • p1p2 が同じメモリを参照。
  • どちらのポインタもスコープを抜けると、メモリが自動解放される。
  • まとめ

  • ポインタはメモリアドレスを扱う特殊な変数。
  • 手動 delete はメモリリークの原因になるため、スマートポインタを活用すべき。
  • std::unique_ptr は所有権を1つだけ持つ。
  • std::shared_ptr は複数の所有者が存在できる。
  • よくある質問(FAQ)

    Q1. ポインタと参照の違いは?

    参照 (int& ref = a;) はエイリアスで、NULL にはできません。ポインタは nullptr にでき、後から指す先を変更できます。

    Q2. std::weak_ptr はどんな時に使う?

    std::shared_ptr で循環参照を防ぐために使います。


    ポインタを正しく使うことで、C++のメモリ管理を自在に扱えるようになります。ぜひ実践してみてください!

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