C++におけるポインタ変数と参照変数の違い

2024-08-23

ポインタ変数参照変数は、どちらも他の変数のメモリアドレスを保持するという意味で似ています。しかし、その使用方法や特性にはいくつかの重要な違いがあります。

ポインタ変数

  • 宣言方法: データ型 *変数名;
  • 値: 変数のアドレスを保持する。
  • 操作:
    • アドレスの変更が可能。
    • *演算子を使って間接参照が可能。
    • ->演算子を使って構造体やクラスのメンバにアクセス可能。
  • NULLポインタ: NULL値を保持することができる。
  • 用途: 動的なメモリ管理、関数への引数の渡し、データ構造の実装など。

参照変数

  • 宣言方法: データ型 &変数名 = 対象変数;
  • 値: 対象変数のエイリアスとして動作する。
  • 操作:
    • アドレスの変更はできない。
    • 対象変数の値を変更すると、参照変数の値も変更される。
    • .演算子を使用して対象変数のメンバにアクセス可能。
  • NULL参照: NULL値を保持できない。
  • 用途: 関数への引数の渡し、クラスのメンバー関数の実装など。
  • ポインタ変数: 柔軟性が高く、アドレスの操作が可能。
  • 参照変数: 対象変数のエイリアスとして動作し、アドレスの変更はできない。

例:

int x = 10;
int *p = &x; // ポインタ変数 p に x のアドレスを格納
int &r = x; // 参照変数 r を x のエイリアスとして宣言

*p = 20; // p を使って x の値を変更
r = 30; // r を使って x の値を変更

cout << x << endl; // 出力: 30



ポインタと参照の例

#include <iostream>

using namespace std;

int main() {
    int x = 10;
    int *p = &x; // ポインタ変数 p に x のアドレスを格納

    cout << "xの値: " << x << endl;
    cout << "pの値 (xのアドレス): " << p << endl;
    cout << "pが指す値: " << *p << endl;

    *p = 20; // p を使って x の値を変更

    cout << "変更後の xの値: " << x << endl;

    return 0;
}
#include <iostream>

using namespace std;

int main() {
    int x = 10;
    int &r = x; // 参照変数 r を x のエイリアスとして宣言

    cout << "xの値: " << x << endl;
    cout << "rの値: " << r << endl;

    r = 20; // r を使って x の値を変更

    cout << "変更後の xの値: " << x << endl;

    return 0;
}

解説

ポインタ変数の例では、px のアドレスを保持しています。*p を使用することで、p が指すメモリ位置の値にアクセスできます。この例では、*p を変更することで、x の値も変更されます。

違い:

  • アドレスの変更: ポインタ変数はアドレスを変更できますが、参照変数はできません。
  • エイリアス: 参照変数は対象変数のエイリアスとして動作します。
  • 演算子: ポインタ変数には * 演算子を使用して間接参照を行います。参照変数には . 演算子を使用して対象変数のメンバにアクセスします。



ポインタと参照の代替方法

ポインタと参照は、C++で変数のアドレスを扱うための重要な概念です。しかし、特定の状況では、これらの代替方法を使用することもできます。

const参照

  • 目的: 対象変数の値を変更できない参照を作成する。
int x = 10;
const int &r = x; // r は x の読み取り専用参照

// r = 20; // エラー: r は const 参照なので変更できません

std::reference_wrapper

  • 目的: 参照を値として扱うことができるようにする。
  • 宣言: std::reference_wrapper<データ型> 変数名 = 対象変数;
int x = 10;
std::reference_wrapper<int> ref = x;

// ref.get() を使って参照先の値にアクセス
cout << ref.get() << endl; // 出力: 10

std::unique_ptr

  • 目的: 自動的なメモリ管理と所有権の管理を行う。
  • 宣言: std::unique_ptr<データ型> 変数名 = std::make_unique<データ型>();
std::unique_ptr<int> ptr = std::make_unique<int>(10);

// ptr がスコープ外になると自動的にメモリが解放される

std::shared_ptr

  • 目的: 複数のオブジェクトが同じメモリを共有できるようにする。
std::shared_ptr<int> ptr1 = std::make_shared<int>(10);
std::shared_ptr<int> ptr2 = ptr1;

// ptr1 と ptr2 が同じメモリを共有している

std::weak_ptr

  • 目的: std::shared_ptr の弱参照を作成する。
  • 宣言: std::weak_ptr<データ型> 変数名 = ptr; // ptr は std::shared_ptr
std::shared_ptr<int> ptr = std::make_shared<int>(10);
std::weak_ptr<int> weak_ptr = ptr;

// weak_ptr は ptr が存在する限り有効

c++ pointers reference

c++ pointers reference

C++におけるキャストの比較: Regular Cast, static_cast, dynamic_cast

C++では、異なるデータ型間で値を変換する操作をキャストと呼びます。キャストには、regular cast、static_cast、dynamic_castの3種類があります。最も単純なキャスト方法です。コンパイル時に型チェックが行われますが、実行時に型安全性が保証されません。


C/C++ ビット操作入門: 単一ビットの設定、クリア、トグルの代替方法

C++とCでは、ビットレベルでの操作を行うことができます。これは、低レベルなシステムプログラミングや、効率的なデータ処理において重要です。ビット演算子& : AND| : OR~ : NOT<< : 左シフト>> : 右シフトビット位置は、通常0から始まり、右から左にインデックスされます。


C++におけるクラスと構造体の使い分け:具体的なコード例

C++では、クラスと構造体はどちらもデータと関数をカプセル化するための手段ですが、その使用目的とデフォルトのアクセス修飾子に違いがあります。デフォルトのアクセス修飾子: private主な用途:オブジェクト指向プログラミング (OOP) における抽象的なデータ型を定義する。データの隠蔽とカプセル化を実現する。継承やポリモーフィズムなどのOOPの概念を活用する。


C++におけるキャストの比較: Regular Cast, static_cast, dynamic_cast

C++では、異なるデータ型間で値を変換する操作をキャストと呼びます。キャストには、regular cast、static_cast、dynamic_castの3種類があります。最も単純なキャスト方法です。コンパイル時に型チェックが行われますが、実行時に型安全性が保証されません。