【C++学習|実務向け】スマートポインタとconst修飾の正しい使い分け:実務で迷わないための指針

1. 導入

C++のスマートポインタを利用する際、const修飾子の配置場所に迷うことはありませんか?「ポインタ自体を書き換えさせたくない場合」と「指し示しているオブジェクトの中身を書き換えさせたくない場合」の区別を曖昧にすると、意図しないバグやコンパイルエラーの原因となります。本記事では、この二つの違いを明確にし、現場で自信を持ってconstを活用するための指針を解説します。

2. 基礎知識

C++のポインタにおけるconstには、大きく分けて「ポインタ自体が定数(ポインタの書き換え不可)」と「指す先が定数(値の書き換え不可)」の2種類があります。
スマートポインタであるstd::unique_ptrやstd::shared_ptrにおいても、テンプレート引数と変数宣言のどちらにconstを付けるかで、この役割が完全に分かれます。

const std::unique_ptr p: ポインタ自体が定数。pの指し先を変更(resetや代入)することはできませんが、pによる値の変更は可能です。
std::unique_ptr p: 指す先が定数。pの指し先を変更することは可能ですが、pによる値の変更はできません。

3. 実装/解決策

実務では「読み取り専用のデータを保持したいのか」あるいは「一度確保したメモリを途中で差し替えてほしくないのか」という設計意図に合わせて使い分けます。特にメンバ変数として保持する場合、constメンバとして定義することで意図しない再代入を防ぐ設計が推奨されます。

4. サンプルプログラム

以下のコードは、二つのconstの挙動の違いを実証するサンプルです。コンパイルを通る箇所と、意図的にエラーになる(コメントアウトしている)箇所を確認してください。

include <iostream>
include <memory>

int main() {
    // 1. ポインタ自体が定数 (ポインタの差し替えが不可)
    const std::unique_ptr<int> ptr_const_ptr = std::make_unique<int>(10);
    ptr_const_ptr = 20; // OK: 中身の変更は可能
    // ptr_const_ptr = std::make_unique<int>(30); // エラー: ポインタの差し替えは不可

    // 2. 指す先が定数 (値の変更が不可)
    std::unique_ptr<const int> ptr_const_val = std::make_unique<const int>(10);
    // ptr_const_val = 20; // エラー: 値の変更は不可
    ptr_const_val = std::make_unique<const int>(30); // OK: ポインタの差し替えは可能

    // 3. 両方を定数にする (どちらも変更不可)
    const std::unique_ptr<const int> ptr_both_const = std::make_unique<const int>(10);

    std::cout << "実行完了" << std::endl;
    return 0;
}

5. 応用・注意点

現場で特に注意すべきは、const std::shared_ptrの扱いです。shared_ptrは参照カウントを管理しているため、constを付けると「参照カウントの更新」を含め、ポインタのコピーや代入が制限されます。関数引数として渡す際は、可能な限り const T& または const T を使い、スマートポインタそのものを引数に取る設計(所有権の移動や共有が必要な場合を除く)を避けることで、より柔軟なコードになります。

また、クラスのメンバ変数として定義する場合は、`std::unique_ptr` を採用することが多くなります。これは「このインスタンスが持つデータは不変である」という強い契約をインターフェースとして表明できるため、堅牢なクラス設計に繋がります。

コメント

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