C++ でこのポインタを使用する
このチュートリアルでは、最初に this
ポインタの概念を理解します。
次に、例を使用してその使用法を示します。最後に、this
が*this
とどのように異なるかを見ていきます。
C++ で this
ポインタを使用する
this
ポインターは、非静的クラスメンバー関数スコープで、呼び出し元オブジェクトを参照または指す、暗黙的に使用可能なポインターです。定義上理解できなかった場合は、概念を理解するためにいくつかの基本事項を見てみましょう。
すべての非静的データメンバーには、オブジェクトごとに個別のコピーがあることがわかっています。ただし、メンバー関数コードはすべてのオブジェクト間で共有されます。
同じクラスのすべてのオブジェクトは、呼び出す必要があるときに、コードセグメントから同じ関数定義にアクセスします。コンパイラは、この特定の呼び出し元オブジェクトに固有の適切なデータメンバーにアクセスまたは更新するために、呼び出し元オブジェクト(現在メンバー関数を呼び出している)情報を知る必要があります。
では、関数の定義が同じであるとすると、コンパイラはどのようにして呼び出し元のオブジェクト情報を取得するのでしょうか。
簡単な答えは、プログラムのコンパイル中に、コンパイラーがメンバー関数に非表示の暗黙のポインターパラメーターを自動的に追加することです。この暗黙のポインタは、this
ポインタとして知られています。
メンバー関数が特定のクラスオブジェクトを介して呼び出されるたびに、呼び出し元のオブジェクトは、this
ポインターへの隠し引数として自動的に提供されます。これで、コンパイラはこのポインタを使用して、呼び出し元オブジェクトに固有の適切なデータメンバーにアクセスまたは変更できます。
this
ポインタは、クラスの静的メンバー(関数およびデータメンバー)がすべてのオブジェクト間で共有されるため、必要ありません。静的メンバーの更新またはアクセスは、どのオブジェクトにも固有ではありません。クラスのオブジェクトを作成せずに、これらのメンバーにアクセスすることもできます。
したがって、コンパイラはこれらのメンバーで this
演算子を使用しません。
コード例を調べて、コンパイラーが暗黙的に this
ポインターを使用すると言っている理由と、それが便利な場合を理解しましょう。
#include <iostream>
#include <string>
using namespace std;
class Person {
private:
string Name;
public:
Person(string Name) {
this->Name = Name; // this->Name is the private member for this object
}
void PrintName() { cout << this->Name << endl; }
void PrintName1() { cout << Name << endl; }
};
int main() {
Person P("Alexa");
P.PrintName();
P.PrintName1();
return 0;
}
上記のコード例では、プライベートデータメンバーName
を出力するための 2つの異なるメンバー関数を持つ Person
クラスを定義しています。main()
関数の最初のステートメントは、Person
オブジェクトを生成し、コンストラクターへの引数として Alexa
を渡します。
現在、コンストラクターの this->Name
は、コンパイラーがローカルパラメーターName
とプライベートデータメンバーName
を区別するのに役立ちます。
main()
の後続のコードは、P を介して printName()
および printName1()
を呼び出します(つまり、P が呼び出し元オブジェクトになります)。
出力:
Alexa
Alexa
両方の関数の出力は同じです。これは、コンパイラが PrintName1()
の this->
の前に Name
を暗黙的に付けるためです。
C++ での this
と*this
の違い
これまでのところ、this
はオブジェクトへのポインタであるという概念は明らかです。タイプ Person
の obj
の場合、this
タイプは Person*
です。
もう 1つ覚えておくべきことは、this
ポインターは常に rvalue
であり、変更できないことです。ただし、*this
は this
ポインタを参照解除します。
十分な背景を経た後、サンプルコードを見て、this
と*this
の違いを理解しましょう。
#include <iostream>
using namespace std;
class Counter {
private:
int Count;
public:
Counter() { this->Count = 0; }
void IncreaseCount() { Count++; }
void PrintCount() { cout << this->Count << endl; }
Counter* GetCount_Pointer() { return this; }
Counter GetCount_Copy() { return *this; }
Counter& GetCount_Reference() { return *this; }
};
int main() {
// Section-A
cout << "Sectio-A" << endl;
Counter C1;
C1.IncreaseCount();
Counter* CounterPtr =
C1.GetCount_Pointer(); // CounterObj will be pointing to C1
CounterPtr->IncreaseCount();
C1.PrintCount();
// Section-B
cout << "Section-B" << endl;
Counter C2;
C2 = C1.GetCount_Copy();
C1.IncreaseCount();
C1.PrintCount();
C2.PrintCount();
// Section-C
cout << "Section-B" << endl;
Counter& CounterRef = C1.GetCount_Reference();
CounterRef.PrintCount();
return 0;
}
このコードスニペットは、GetCount
メソッドへの複数のメソッドを持つ Counter
クラスを作成します。最初のメソッド(つまり、GetCount_Pointer
)は、単に呼び出し元オブジェクトのアドレスである this
ポインターの値を返します。
GetCount_Copy
メソッドは*this
を返しますが、return タイプは Counter
タイプのオブジェクトです。したがって、この関数は呼び出し元オブジェクトのディープコピーを返します。つまり、返されたコピーを変更しても、元のコピーには影響しません。
最後の GetCount_Reference
メソッドも*this
を返しますが、このメソッドの戻り型は参照オブジェクト(つまり、Counter&
)です。新しいディープコピーを作成する代わりに、コンパイラは元のオブジェクトへのエイリアスまたは参照を返します。
エイリアスを介して行われた変更は、元のオブジェクトにも反映されます。
メインのドライバーコードについて説明する前に、プログラムの出力を見てみましょう。
出力:
Sectio-A
2
Section-B
3
2
Section-B
3
main
メソッドのセクション A は、最初に Counter
タイプの C1
オブジェクトを宣言し、IncreaseCount()
を介してその Count
をインクリメントします。後で、C1.GetCount_Pointer()
によって返されたアドレスで CounterPtr
ポインタを初期化します。
これで、CounterPtr
は C1
を指します。したがって、CounterPtr
を介してインクリメント関数を呼び出すと、C1
も変更されます。
セクション B の C2 = C1.GetCount_Copy()
は、C1.GetCount_Copy()
が C1
のコピーに置き換えられるため、C1
の現在のすべての内容を C2
に深くコピーします。したがって、C1
のカウントを増やしても C2
には影響しません。
セクション C は、Counter
タイプへの参照変数を宣言し、C1.GetCount_Reference()
によって返されるもので初期化します。C1.GetCount_Reference()
は C1
のエイリアスを返すため、CounterRef
は C1
の別名になります。
ステートメント Counter& CounterRef = C1.GetCount_Reference();
論理的には Counter& CounterRef = C1
と同等になります。
Husnain is a professional Software Engineer and a researcher who loves to learn, build, write, and teach. Having worked various jobs in the IT industry, he especially enjoys finding ways to express complex ideas in simple ways through his content. In his free time, Husnain unwinds by thinking about tech fiction to solve problems around him.
LinkedIn