閉じる

【C++】thisポインタの使い方と仕組みを徹底解説!活用例や注意点も紹介

C++でクラスを扱う際に、避けて通れない非常に重要な概念がthisポインタです。

オブジェクト指向プログラミングにおいて、自分自身のインスタンスを指し示すこの機能は、メンバ変数へのアクセスやメソッドチェーンの実現など、多様な場面で活用されます。

本記事では、初心者の方から中級者の方までを対象に、thisポインタの基本的な仕組みから実践的なテクニック、そして注意点までを詳しく解説します。

thisポインタとは何か

C++におけるthisポインタとは、メンバ関数内で「その関数を呼び出しているオブジェクト自身」のアドレスを保持している隠れたポインタのことです。

クラスの定義内であればどこでも利用可能であり、実行時にどのインスタンスが操作されているかを識別するために使われます。

通常、メンバ変数にアクセスする際にはわざわざthis->と記述する必要はありませんが、コンパイラは内部的にこのポインタを利用して特定のオブジェクトのデータにアクセスしています。

つまり、すべての非静的メンバ関数には、暗黙のうちにthisポインタが引数として渡されているのです。

thisポインタの基本的な性質

thisポインタには、プログラミングを行う上で理解しておくべきいくつかの性質があります。

特徴詳細
そのクラス自体のポインタ型 (例: クラス名*)
変更不可thisポインタ自体の値を書き換えることはできない (rvalue)
静的関数での利用staticメンバ関数の中では使用できない
自動定義プログラマが定義しなくても自動的に利用可能になる

このように、thisポインタは特殊な「読み取り専用のローカル変数」のような振る舞いをします。

thisポインタが動く仕組み

なぜ、複数のオブジェクトが存在しても、メンバ関数は正しいデータにアクセスできるのでしょうか。

その理由は、C++のコンパイラが関数呼び出しを変換する手法にあります。

コンパイラによる暗黙的な引数追加

私たちがメンバ関数を呼び出すとき、文法上は「オブジェクト名.関数名()」と書きます。

しかし、コンピュータ内部では関数の第一引数にそのオブジェクトのアドレスがこっそり渡されています

例えば、以下のようなクラスがあるとします。

C++
class Player {
public:
    int health;
    void damage(int amount) {
        health -= amount; // 内部では this->health -= amount; となっている
    }
};

このdamage関数は、内部的には概念として次のように処理されています。

C++
// コンパイラが解釈するイメージ
void damage(Player* const this, int amount) {
    this->health -= amount;
}

このように、呼び出し元のオブジェクトのアドレスをthisとして受け取ることで、関数はどのインスタンスのhealthを減らすべきかを判断できるのです。

thisポインタの主な活用シーン

thisポインタは自動的に処理されるため、意識しなくてもプログラムは動きます。

しかし、明示的にthisと記述しなければならない場面がいくつか存在します。

1. メンバ変数と引数の名前衝突を避ける

最も一般的な使い道は、コンストラクタやセッター関数において、引数名とメンバ変数が同じ名前になった場合の識別です。

C++
#include <iostream>
#include <string>

class Student {
   private:
    std::string name;
    int age;

   public:
    // 引数名とメンバ変数が同じ「name」「age」
    Student(std::string name, int age) {
        // name = name; と書くと引数に引数を代入するだけになる
        this->name = name;
        // 左辺がメンバ変数、右辺が引数
        this->age = age;
    }

    void display() {
        std::cout << "名前: " << name << ", 年齢: " << age << std::endl;
    }
};

int main() {
    Student s("田中", 20);
    s.display();
    return 0;
}
実行結果
名前: 田中, 年齢: 20

この例では、this->nameと書くことで「このオブジェクトのメンバ変数であるname」であることを明示しています。

これにより、スコープが優先される引数のnameと区別が可能になります。

2. メソッドチェーンの実現

複数のメンバ関数をドットで繋げて連続して呼び出す「メソッドチェーン」を実装する際にも、thisポインタが不可欠です。

メソッドチェーンを実現するには、関数の戻り値として*this(自分自身の実体)を参照返しします。

C++
#include <iostream>

class Calculator {
private:
    int value;

public:
    Calculator() : value(0) {}

    // 自分自身を返すために Calculator& を戻り値にする
    Calculator& add(int n) {
        value += n;
        return *this; // 自身の参照を返す
    }

    Calculator& multiply(int n) {
        value *= n;
        return *this;
    }

    void show() {
        std::cout << "結果: " << value << std::endl;
    }
};

int main() {
    Calculator calc;
    // メソッドチェーンで記述
    calc.add(10).multiply(3).add(5).show();

    return 0;
}
実行結果
結果: 35

このコードでは、add関数が終了したあとに*thisが返るため、そのまま続けて.multiplyを呼び出すことができます。

3. 演算子オーバーロードでの利用

コピー代入演算子(operator=)を独自に定義する場合、自己代入のチェックを行うためにthisポインタのアドレスを比較することがあります。

C++
class MyBuffer {
    int* data;
public:
    MyBuffer& operator=(const MyBuffer& other) {
        // 自分自身を自分に代入しようとしていないかチェック
        if (this == &other) {
            return *this;
        }
        
        // メモリの再確保などの処理...
        return *this;
    }
};

もしthis == &otherであれば、同じオブジェクトのアドレスを指しているため、無駄なコピー処理やメモリ解放によるエラーを防ぐことができます。

constメンバ関数とthisポインタ

メンバ関数にconst修飾子を付けた場合、thisポインタの性質が変化します。

これは、安全なプログラムを書く上で非常に重要なポイントです。

通常のメンバ関数内では、thisの型はT* const(T型のオブジェクトを指す、変更不可なポインタ)です。

しかし、constメンバ関数内では、thisの型は const T* const に変わります

これにより、constメンバ関数内ではメンバ変数の値を書き換えることができなくなります

C++
class Display {
    int brightness;
public:
    // constが付いているため、thisは const Display* 型になる
    void show() const {
        // brightness = 100; // コンパイルエラー!書き換え不可
        std::cout << brightness << std::endl;
    }
};

ラムダ式におけるthisのキャプチャ

モダンなC++(C++11以降)では、ラムダ式の中でクラスのメンバにアクセスするために、thisポインタを「キャプチャ」する必要があります。

thisをキャプチャする2つの方法

[this]

現在のオブジェクトをポインタとしてキャプチャします。

最も一般的ですが、オブジェクトの寿命が切れた後にラムダが実行されるとクラッシュする危険があります。

[*this]

(C++17以降): 現在のオブジェクトのコピーを作成してキャプチャします。

非同期処理などでオブジェクトが消える可能性がある場合に安全です。

C++
#include <iostream>
#include <vector>
#include <algorithm>

class Worker {
    int id = 101;
public:
    void doTask() {
        auto lambda = [this]() {
            // thisをキャプチャすることでメンバ変数にアクセス可能
            std::cout << "Working on ID: " << id << std::endl;
        };
        lambda();
    }
};

thisポインタを使用する際の注意点

thisポインタは非常に便利ですが、誤った使い方をすると重大なバグの原因になります。

staticメンバ関数では使えない

static(静的)メンバ関数は特定のインスタンスに関連付けられていないため、thisポインタを持ちません。

static関数内から非staticなメンバ変数にアクセスしようとすると、参照すべきインスタンスが不明なためコンパイルエラーとなります。

delete this という危険な操作

稀にdelete this;というコードが見られます。

これは「自分自身をメモリから消去する」という操作です。

  • 絶対にスタック上で生成されたオブジェクトに対して行ってはいけません。newで生成されたもの限定)
  • 実行後、そのオブジェクトのメンバには一切アクセスしてはいけません。
  • 非常にリスクが高いため、スマートポインタが普及した現代のC++では、特別な理由がない限り推奨されません。

ヌルポインタによるアクセス

理論上、thisがNULLになることはありません。

しかし、nullptrに対してメンバ関数を呼び出す(未定義動作)コードを書いてしまうと、その関数内のthisはNULLを指すことになります。

C++
Player* p = nullptr;
p->damage(10); // 実行時にクラッシュする可能性が高い

このような事態を防ぐため、オブジェクトの有効性を常に確認することが重要です。

まとめ

thisポインタは、オブジェクト自身とメンバ関数を結びつける架け橋のような存在です。

コンパイラが自動で処理してくれるため、普段はその存在を強く意識する必要はありませんが、名前の衝突回避、メソッドチェーンの実装、ラムダ式での利用など、高度なプログラミングには欠かせない要素です。

特にconst修飾時や、モダンC++におけるキャプチャの振る舞いを正しく理解しておくことで、メモリ安全で読みやすいコードを書くことができるようになります。

今回学んだ「自分自身を指す」という直感的なイメージを大切に、今後のクラス設計に役立ててみてください。

クラスの定義と基本

クラウドSSLサイトシールは安心の証です。

URLをコピーしました!