[C++] thisをつけるべきケースを解説

C++においてthisは、クラスのメンバ関数内でオブジェクト自身を指すポインタです。

thisを明示的に使うべきケースは、主にメンバ変数と引数の名前が同じ場合です。

例えば、コンストラクタやセッターメソッドで、引数名とメンバ変数名が同じ場合、thisを使ってメンバ変数を区別します。

this->変数名とすることで、引数ではなくオブジェクトのメンバ変数を指していることを明示できます。

この記事でわかること
  • thisの使い方とその重要性
  • メンバ変数と引数の衝突の回避法
  • 静的メンバ関数の特性について
  • デザインパターンにおけるthisの応用
  • thisを使う際の注意点とリスク

目次から探す

thisを使うべきケース

メンバ変数と引数の名前が同じ場合

メンバ変数と引数の名前が同じ場合、thisを使うことで明示的にメンバ変数を参照できます。

これにより、引数とメンバ変数の混同を避けることができます。

以下はその例です。

#include <iostream>
using namespace std;
class Example {
private:
    int value; // メンバ変数
public:
    // コンストラクタ
    Example(int value) {
        this->value = value; // 引数とメンバ変数が同じ名前
    }
    void display() {
        cout << "Value: " << value << endl;
    }
};
int main() {
    Example ex(10);
    ex.display(); // 出力: Value: 10
    return 0;
}
出力: Value: 10

メンバ関数内でオブジェクト自身を返す場合

メンバ関数内でオブジェクト自身を返す場合、thisを使うことで、現在のオブジェクトを返すことができます。

これにより、メソッドチェーンを実現することが可能です。

以下はその例です。

#include <iostream>
using namespace std;
class Example {
private:
    int value;
public:
    Example(int value) : value(value) {}
    // 自身を返すメソッド
    Example* setValue(int value) {
        this->value = value;
        return this; // 自身を返す
    }
    void display() {
        cout << "Value: " << value << endl;
    }
};
int main() {
    Example ex(10);
    ex.setValue(20)->display(); // 出力: Value: 20
    return 0;
}
出力: Value: 20

メソッドチェーンを実現する場合

メソッドチェーンを実現するためには、thisを使ってオブジェクト自身を返す必要があります。

これにより、複数のメソッドを連続して呼び出すことができます。

以下はその例です。

#include <iostream>
using namespace std;
class Example {
private:
    int value;
public:
    Example(int value) : value(value) {}
    Example* add(int amount) {
        this->value += amount;
        return this; // 自身を返す
    }
    Example* subtract(int amount) {
        this->value -= amount;
        return this; // 自身を返す
    }
    void display() {
        cout << "Value: " << value << endl;
    }
};
int main() {
    Example ex(10);
    ex.add(5)->subtract(3)->display(); // 出力: Value: 12
    return 0;
}
出力: Value: 12

ポインタを返すメソッドでthisを使う場合

ポインタを返すメソッドでは、thisを使ってオブジェクトのポインタを返すことができます。

これにより、オブジェクトのアドレスを他の場所で利用することが可能です。

以下はその例です。

#include <iostream>
using namespace std;
class Example {
private:
    int value;
public:
    Example(int value) : value(value) {}
    // ポインタを返すメソッド
    Example* getPointer() {
        return this; // 自身のポインタを返す
    }
    void display() {
        cout << "Value: " << value << endl;
    }
};
int main() {
    Example ex(10);
    Example* ptr = ex.getPointer();
    ptr->display(); // 出力: Value: 10
    return 0;
}
出力: Value: 10

thisを使わなくても良いケース

名前の衝突がない場合

メンバ変数と引数の名前が異なる場合、thisを使わなくてもメンバ変数にアクセスできます。

この場合、コードがシンプルになり、可読性が向上します。

以下はその例です。

#include <iostream>
using namespace std;
class Example {
private:
    int value; // メンバ変数
public:
    // コンストラクタ
    Example(int initialValue) {
        value = initialValue; // 引数とメンバ変数が異なる
    }
    void display() {
        cout << "Value: " << value << endl;
    }
};
int main() {
    Example ex(10);
    ex.display(); // 出力: Value: 10
    return 0;
}
出力: Value: 10

グローバル変数や静的メンバ変数との区別が明確な場合

グローバル変数や静的メンバ変数とメンバ変数が異なる場合、thisを使わなくても明確に区別できます。

この場合も、コードが簡潔になります。

以下はその例です。

#include <iostream>
using namespace std;
int globalValue = 5; // グローバル変数
class Example {
private:
    static int staticValue; // 静的メンバ変数
    int value; // メンバ変数
public:
    Example(int value) : value(value) {}
    void display() {
        cout << "Global Value: " << globalValue << endl;
        cout << "Static Value: " << staticValue << endl;
        cout << "Member Value: " << value << endl;
    }
};
int Example::staticValue = 10; // 静的メンバ変数の初期化
int main() {
    Example ex(20);
    ex.display(); // 出力: Global Value: 5, Static Value: 10, Member Value: 20
    return 0;
}
出力: Global Value: 5
Static Value: 10
Member Value: 20

単純なメンバ関数内でのアクセス

単純なメンバ関数内でメンバ変数にアクセスする場合、thisを使わなくても問題ありません。

特に、メンバ変数が一つだけの場合は、thisを使わずに直接アクセスすることが一般的です。

以下はその例です。

#include <iostream>
using namespace std;
class Example {
private:
    int value; // メンバ変数
public:
    Example(int value) : value(value) {}
    void increment() {
        value++; // 直接メンバ変数にアクセス
    }
    void display() {
        cout << "Value: " << value << endl;
    }
};
int main() {
    Example ex(10);
    ex.increment();
    ex.display(); // 出力: Value: 11
    return 0;
}
出力: Value: 11

thisを使う際の注意点

thisはポインタであることに注意

thisは、オブジェクト自身を指すポインタであるため、使用する際にはポインタとしての特性を理解しておく必要があります。

thisを使うときは、ポインタ演算やメンバ関数の呼び出しに注意が必要です。

以下はその例です。

#include <iostream>
using namespace std;
class Example {
private:
    int value;
public:
    Example(int value) : value(value) {}
    void increment() {
        this->value++; // thisはポインタなので、valueにアクセスする際は*this->valueでも可
    }
    void display() {
        cout << "Value: " << value << endl;
    }
};
int main() {
    Example ex(10);
    ex.increment();
    ex.display(); // 出力: Value: 11
    return 0;
}
出力: Value: 11

コンストラクタやデストラクタでのthisの扱い

コンストラクタやデストラクタ内でthisを使用することは可能ですが、注意が必要です。

特に、デストラクタ内でthisを使って他のメンバ関数を呼び出すと、オブジェクトが既に破棄されている可能性があるため、未定義の動作を引き起こすことがあります。

以下はその例です。

#include <iostream>
using namespace std;
class Example {
private:
    int value;
public:
    Example(int value) : value(value) {
        cout << "Constructor called: " << this->value << endl;
    }
    ~Example() {
        cout << "Destructor called: " << this->value << endl; // 注意が必要
    }
};
int main() {
    Example ex(10); // 出力: Constructor called: 10
    return 0; // 出力: Destructor called: 10
}
出力: Constructor called: 10
Destructor called: 10

静的メンバ関数ではthisが使えない理由

静的メンバ関数は、クラスのインスタンスに依存しないため、thisを使用することができません。

静的メンバ関数は、クラス全体に対して共通の機能を提供するために設計されています。

以下はその例です。

#include <iostream>
using namespace std;
class Example {
public:
    static void staticFunction() {
        // thisは使えない
        cout << "Static function called." << endl;
    }
};
int main() {
    Example::staticFunction(); // 出力: Static function called.
    return 0;
}
出力: Static function called.

thisを使った自己代入のリスク

thisを使って自己代入を行うと、意図しない動作を引き起こす可能性があります。

特に、オブジェクトの状態が変更される前に自己代入を行うと、予期しない結果を招くことがあります。

以下はその例です。

#include <iostream>
using namespace std;
class Example {
private:
    int value;
public:
    Example(int value) : value(value) {}
    void selfAssign() {
        this->value = this->value; // 自己代入
        cout << "Self-assigned Value: " << value << endl; // 意図しない動作
    }
};
int main() {
    Example ex(10);
    ex.selfAssign(); // 出力: Self-assigned Value: 10
    return 0;
}
出力: Self-assigned Value: 10

自己代入は通常、特にポインタや参照を扱う場合には注意が必要です。

応用例:thisを使ったデザインパターン

メソッドチェーンを使ったビルダーパターン

ビルダーパターンでは、オブジェクトの構築を段階的に行うためにメソッドチェーンを利用します。

thisを使うことで、各メソッドがオブジェクト自身を返し、連続して呼び出すことが可能になります。

以下はその例です。

#include <iostream>
#include <string>
using namespace std;
class Builder {
private:
    string name;
    int age;
public:
    Builder& setName(const string& name) {
        this->name = name; // thisを使って自身を返す
        return *this;
    }
    Builder& setAge(int age) {
        this->age = age; // thisを使って自身を返す
        return *this;
    }
    void build() {
        cout << "Name: " << name << ", Age: " << age << endl;
    }
};
int main() {
    Builder builder;
    builder.setName("Alice").setAge(30).build(); // 出力: Name: Alice, Age: 30
    return 0;
}
出力: Name: Alice, Age: 30

thisを使ったフルエントインターフェース

フルエントインターフェースは、メソッドチェーンを利用してオブジェクトの状態を設定するための設計パターンです。

thisを使うことで、各メソッドがオブジェクト自身を返し、直感的なインターフェースを提供します。

以下はその例です。

#include <iostream>
using namespace std;
class FluentInterface {
private:
    string color;
    string size;
public:
    FluentInterface& setColor(const string& color) {
        this->color = color; // thisを使って自身を返す
        return *this;
    }
    FluentInterface& setSize(const string& size) {
        this->size = size; // thisを使って自身を返す
        return *this;
    }
    void display() {
        cout << "Color: " << color << ", Size: " << size << endl;
    }
};
int main() {
    FluentInterface item;
    item.setColor("Red").setSize("Large").display(); // 出力: Color: Red, Size: Large
    return 0;
}
出力: Color: Red, Size: Large

thisを使ったシングルトンパターン

シングルトンパターンでは、クラスのインスタンスが一つだけであることを保証します。

thisを使って、インスタンスを取得するメソッドを実装することが一般的です。

以下はその例です。

#include <iostream>
using namespace std;
class Singleton {
private:
    static Singleton* instance; // インスタンスを保持するポインタ
    // コンストラクタをプライベートにする
    Singleton() {}
public:
    static Singleton* getInstance() {
        if (instance == nullptr) {
            instance = new Singleton(); // インスタンスを生成
        }
        return instance; // thisを使ってインスタンスを返す
    }
};
Singleton* Singleton::instance = nullptr; // インスタンスの初期化
int main() {
    Singleton* s1 = Singleton::getInstance();
    Singleton* s2 = Singleton::getInstance();
    cout << (s1 == s2) << endl; // 出力: 1 (同じインスタンスを指す)
    return 0;
}
出力: 1

thisを使ったオブジェクトのコピー

オブジェクトのコピーを行う際に、thisを使って自身のメンバ変数を設定することができます。

これにより、コピーコンストラクタや代入演算子を実装する際に、明確に自身のメンバ変数にアクセスできます。

以下はその例です。

#include <iostream>
using namespace std;
class Example {
private:
    int value;
public:
    Example(int value) : value(value) {}
    // コピーコンストラクタ
    Example(const Example& other) {
        this->value = other.value; // thisを使って自身にコピー
    }
    void display() {
        cout << "Value: " << value << endl;
    }
};
int main() {
    Example ex1(10);
    Example ex2 = ex1; // コピーコンストラクタが呼ばれる
    ex2.display(); // 出力: Value: 10
    return 0;
}
出力: Value: 10

よくある質問

thisを使わないとどうなるのか?

thisを使わない場合、特にメンバ変数と引数の名前が同じであるとき、意図しない動作を引き起こす可能性があります。

引数が優先され、メンバ変数が更新されないことがあります。

これにより、プログラムのバグや予期しない結果を招くことがあります。

例えば、以下のようなコードでは、thisを使わないと引数がメンバ変数に代入されず、意図した動作をしません。

例:Example(int value) { value = value; }

静的メンバ関数でthisが使えないのはなぜ?

静的メンバ関数は、クラスのインスタンスに依存しないため、thisを使用することができません。

静的メンバ関数は、クラス全体に対して共通の機能を提供するために設計されており、特定のオブジェクトに関連付けられていないため、thisポインタが存在しません。

したがって、静的メンバ関数内では、インスタンスメンバにアクセスすることはできません。

thisを使うことでパフォーマンスに影響はあるか?

一般的に、thisを使うこと自体がパフォーマンスに大きな影響を与えることはありません。

thisはポインタであり、メンバ変数やメンバ関数にアクセスする際に必要なオーバーヘッドは非常に小さいため、通常は無視できるレベルです。

ただし、thisを多用することでコードが複雑になり、可読性が低下する可能性があるため、適切に使用することが重要です。

まとめ

この記事では、C++におけるthisの使い方やその重要性について詳しく解説しました。

特に、thisを使うべきケースや使わなくても良いケース、さらにはthisを使用する際の注意点や応用例についても触れました。

C++プログラミングにおいて、thisを適切に活用することで、より明確で効率的なコードを書くことが可能になりますので、ぜひ実際のプログラムに取り入れてみてください。

  • URLをコピーしました!
目次から探す