フリーキーズ | 独学プログラミング

C++のthisポインタとは?仕組みを図解で初心者にもわかりやすく解説

リンドくん

リンドくん

たなべ先生、C++を勉強してるんですが、「thisポインタ」って何ですか?なんだか難しそうで...

たなべ

たなべ

thisポインタは最初は複雑に見えるけど、実は「自分自身を指す特別なポインタ」なんだよ。
RPGで例えると、キャラクターが「俺は〜」って自分のことを指すときの「俺」みたいなものかな。

C++のオブジェクト指向プログラミングを学んでいると、必ず出会うのが「thisポインタ」という概念です。
多くの初学者が「なぜこんなものが必要なの?」「どういう仕組みになっているの?」と疑問に思うのではないでしょうか?

thisポインタは、C++においてクラスのメンバ関数が「自分自身のオブジェクト」を参照するための特別な仕組みです。
この記事では、thisポインタの基本概念から実践的な使い方まで、図解とサンプルコードを使って初心者の方にもわかりやすく解説していきます。

プログラミング学習でお悩みの方へ

HackATAは、エンジニアを目指す方のためのプログラミング学習コーチングサービスです。 経験豊富な現役エンジニアがあなたの学習をサポートします。

✓ 質問し放題

✓ β版公開中(2025年内の特別割引)

HackATAの詳細を見る

thisポインタの基本概念

リンドくん

リンドくん

そもそも、なぜ「自分自身を指すポインタ」が必要なんですか?

たなべ

たなべ

実は、C++ではメンバ関数が呼び出されるとき、どのオブジェクトのメンバ関数なのかを識別する必要があるんだ。
同じクラスから作られた複数のオブジェクトがあるとき、「今、どのオブジェクトの関数が呼ばれているのか」を知るためにthisポインタが使われるんだよ。

thisポインタとは何か

thisポインタは、C++において各メンバ関数に自動的に渡される暗黙的なポインタです。
このポインタは、現在そのメンバ関数を呼び出しているオブジェクト自身のアドレスを指しています。

簡単に言うと、クラスのメンバ関数内で「この関数を呼び出したオブジェクト」を参照するためのポインタです。

なぜthisポインタが必要なのか

同じクラスから複数のオブジェクトを作成した場合を考えてみましょう。

class Student
{
private:
    string name;
    int age;
public:
    void setName(string newName)
    {
        name = newName;  // どのオブジェクトのnameなのか?
    }
};

int main()
{
    Student tanaka;
    Student suzuki;
    
    tanaka.setName("田中");  // 田中オブジェクトのnameを変更
    suzuki.setName("鈴木");  // 鈴木オブジェクトのnameを変更
}

この例では、setName関数が呼ばれたとき、どのオブジェクトのnameを変更すべきかを識別する必要があります。
thisポインタは、まさにこの問題を解決するために存在しているのです。

thisポインタの型と特徴

thisポインタにはいくつかの重要な特徴があります。

  • : クラス名*(そのクラスへのポインタ)
  • 自動的に渡される: プログラマが明示的に定義する必要はない
  • constメンバ関数では: const クラス名*型になる
  • staticメンバ関数では使用不可: クラス全体に属するため、特定のオブジェクトと関連しない

これらの特徴を理解することで、thisポインタがどのような場面で活用できるかが見えてきます。

thisポインタの仕組みを図解で理解

メモリ上でのオブジェクトの配置

まず、メモリ上でオブジェクトがどのように配置されるかを理解しましょう。

class Rectangle
{
private:
    int width;
    int height;
public:
    void setSize(int w, int h)
    {
        width = w;
        height = h;
    }
    int getArea()
    {
        return width * height;
    }
};

メモリ上では以下のように配置されます。

Rectangleオブジェクトのメモリ上の配置

Rectangleオブジェクトのメモリ上の配置

thisポインタの動作メカニズム

メンバ関数が呼ばれるとき、実際には以下のような処理が内部で行われています。

// プログラマが書くコード
rect1.setSize(10, 20);

// 内部的にはこのような処理が行われる
Rectangle::setSize(&rect1, 10, 20);  // thisに&rect1が渡される

つまり、setSize関数内では以下のような状態になっています。

void setSize(int w, int h)
{
    // this == &rect1 (rect1のアドレス)
    this->width = w;   // rect1.width = w; と同じ
    this->height = h;  // rect1.height = h; と同じ
}

この仕組みにより、同じメンバ関数でもどのオブジェクトに対して操作を行うかが明確に区別されるのです。

実際のメモリアクセス

thisポインタを使ったメンバアクセスは、実際には以下のようなメモリアクセスになります。

// thisが0x1000を指している場合
this->width = 10;    // アドレス0x1000に10を書き込み
this->height = 20;   // アドレス0x1004に20を書き込み

// thisが0x2000を指している場合  
this->width = 30;    // アドレス0x2000に30を書き込み
this->height = 40;   // アドレス0x2004に40を書き込み

これにより、同じコードでも異なるオブジェクトに対して正確に操作を行うことができるのです。

thisポインタの実践的な使用例

リンドくん

リンドくん

thisポインタって、実際にはどんなときに使うんですか?
普通にメンバ変数に直接アクセスすればいいような気がするんですが...

たなべ

たなべ

確かに多くの場合は直接アクセスで十分だね。
でも、メンバ変数と引数の名前が同じときや、オブジェクト自身を返したいときなど、thisポインタが必要不可欠な場面があるんだよ。

パターン① 引数とメンバ変数の名前が同じ場合

最もよく使われるケースが、セッター関数で引数名とメンバ変数名が同じ場合です。

class Person
{
private:
    string name;
    int age;
    
public:
    // 引数名とメンバ変数名が同じ場合
    void setName(string name)
    {
        this->name = name;  // thisポインタで明確に区別
    }
    
    void setAge(int age) {
        this->age = age;    // thisポインタで明確に区別
    }
    
    // thisポインタを使わない場合(エラーになる)
    void setBadName(string name)
    {
        name = name;  // 引数の代入になってしまう(意味がない)
    }
};

このように、thisポインタを使うことでメンバ変数と引数を明確に区別できます。

パターン② メソッドチェーンの実装

オブジェクト自身を返すことで、メソッドを連続して呼び出せるパターンです。

class Calculator
{
private:
    double value;
    
public:
    Calculator(double initial = 0) : value(initial) {}
    
    // thisポインタを使ってオブジェクト自身を返す
    Calculator& add(double n)
    {
        value += n;
        return *this;  // 自分自身を返す
    }
    
    Calculator& multiply(double n)
    {
        value *= n;
        return *this;  // 自分自身を返す
    }
    
    Calculator& subtract(double n)
    {
        value -= n;
        return *this;  // 自分自身を返す
    }
    
    double getValue() const
    {
        return value;
    }
};

int main() {
    Calculator calc(10);
    
    // メソッドチェーンで連続実行
    double result = calc.add(5)
                        .multiply(2)
                        .subtract(3)
                        .getValue();
    
    cout << "結果: " << result << endl;  // 結果: 27
}

一連の操作を直感的に記述できるため、コードの可読性が大幅に向上します。

パターン③ 自己代入の検証

代入演算子のオーバーロードで、自分自身への代入をチェックする場合です。

class MyString
{
private:
    char* data;
    size_t length;
    
public:
    // 代入演算子のオーバーロード
    MyString& operator=(const MyString& other)
    {
        // 自己代入のチェック
        if (this == &other)
        {
            return *this;  // 自分自身なら何もしない
        }
        
        // 既存のメモリを解放
        delete[] data;
        
        // 新しいメモリを確保してコピー
        length = other.length;
        data = new char[length + 1];
        strcpy(data, other.data);
        
        return *this;
    }
};

この自己代入チェックがないと、自分自身に代入したときにメモリが破壊される危険性があります。

パターン④ コピーコンストラクタとの組み合わせ

class Vector2D
{
private:
    double x, y;
    
public:
    Vector2D(double x = 0, double y = 0) : x(x), y(y) {}
    
    // ベクトルの加算
    Vector2D& operator+=(const Vector2D& other)
    {
        this->x += other.x;
        this->y += other.y;
        return *this;
    }
    
    // 正規化(長さを1にする)
    Vector2D& normalize()
    {
        double len = sqrt(x*x + y*y);
        if (len > 0) {
            this->x /= len;
            this->y /= len;
        }
        return *this;
    }
    
    void print() const
    {
        cout << "(" << x << ", " << y << ")" << endl;
    }
};

int main()
{
    Vector2D v1(3, 4);
    Vector2D v2(1, 2);
    
    v1 += v2;          // v1に v2を加算
    v1.normalize();    // v1を正規化
    v1.print();        // 結果を表示
}

thisポインタ使用時の注意点とベストプラクティス

リンドくん

リンドくん

thisポインタを使うときに気をつけることはありますか?

たなべ

たなべ

いくつか重要な注意点があるよ。
特にstaticメンバ関数では使えないことと、不必要に使いすぎないことは覚えておいてほしいな。

注意点① staticメンバ関数では使用不可

staticメンバ関数は特定のオブジェクトに属さないため、thisポインタは使用できません。

class MathUtils
{
public:
    static double pi()
    {
        // return this->getValue();  // エラー!staticメンバ関数でthisは使用不可
        return 3.14159;
    }
    
    double getValue()
    {
        return this->someValue;  // OK:非staticメンバ関数ではthis使用可能
    }
};

注意点② 不必要な使用は避ける

thisポインタは必要な場面でのみ使用し、不必要に使いすぎないことが重要です。

class Student
{
private:
    string name;
    int score;
    
public:
    // 良い例:必要な場面でのみ使用
    void setName(string name)
    {
        this->name = name;  // 引数名と同じなので必要
    }
    
    // あまり良くない例:不必要にthisを使用
    void printInfo()
    {
        cout << this->name << endl;   // 不要(nameだけで十分)
        cout << this->score << endl;  // 不要(scoreだけで十分)
    }
    
    // 推奨:シンプルに記述
    void printInfo()
    {
        cout << name << endl;   // こちらの方が読みやすい
        cout << score << endl;
    }
};

注意点③ nullポインタチェックは不要

thisポインタは通常のメンバ関数呼び出しでは必ず有効なアドレスを指すため、nullチェックは不要です。

class MyClass
{
public:
    void someMethod()
    {
        // 不要なチェック
        if (this != nullptr) {  // 通常は不要
            // 処理
        }
        
        // 通常はそのまま使用してOK
        // 処理
    }
};

ベストプラクティス

  1. 引数名とメンバ変数名が同じときのみ使用
  2. メソッドチェーンを実装するときに使用
  3. 自己代入チェックで使用
  4. コードの可読性を重視し、不必要な使用は避ける

まとめ

リンドくん

リンドくん

thisポインタのこと、だいぶ理解できました!
特にメソッドチェーンの例が面白かったです。

たなべ

たなべ

thisポインタはC++のオブジェクト指向プログラミングの根幹に関わる重要な概念なんだ。
これを理解することで、より柔軟で効率的なプログラムが書けるようになるよ。

今回は、C++のthisポインタについて基本概念から実践的な使用例まで詳しく解説してきました。
thisポインタは一見複雑に見えますが、その仕組みを理解すれば非常に強力なツールとなります。

重要なポイント

  • thisポインタは現在のオブジェクトを指す特別なポインタ
  • 引数名とメンバ変数名が同じときの区別に必須
  • メソッドチェーンの実装で威力を発揮
  • 自己代入の検証で安全性を向上
  • staticメンバ関数では使用不可

特にメソッドチェーンパターンは、モダンなC++プログラミングでよく使われる手法で、ライブラリ設計などで重要な役割を果たします。
thisポインタをマスターすることで、より読みやすく、保守性の高いC++コードが書けるようになります。ぜひ実際のプロジェクトで活用してみてください。

この記事をシェア

関連するコンテンツ