How to Use the this Pointer in C++

Muhammad Husnain Feb 02, 2024
  1. Use the this Pointer in C++
  2. Difference Between this and *this in C++
How to Use the this Pointer in C++

In this tutorial, we will first understand the concept of the this pointer.

Then, we will demonstrate its uses with the help of examples. Lastly, we will see how this differs from *this.

Use the this Pointer in C++

The this pointer is simply an implicitly available pointer that, in a non-static class member function scope, refers or points to the calling object. If you didn’t get it by definition, let’s look at some fundamentals to understand the concept.

We know that all the non-static data members have a separate copy for each object. However, the member functions code is shared among all the objects.

All the objects of the same class access the same function definition from the code segment when they need to invoke it. The compiler needs to know the calling object (who is currently calling a member function) information to access or update the right data member specific to this particular calling object.

So, given that the function definitions are the same, how does the compiler get the calling object information?

The simple answer is that during the compilation of the program, the compiler automatically adds a hidden implicit pointer parameter in the member functions. This implicit pointer is known to be as this pointer.

Whenever a member function gets invoked through a particular class object, the calling object is automatically supplied as a hidden argument to this pointer. Now, the compiler can use this pointer to access or modify the right data member specific to the calling object.

Note
The this pointer is not required with the static members of the class as these members (functions and data members) are shared among all objects.

The update or access to the static members is not specific to any object. We can even access these members without creating an object of the class.

Therefore, the compiler does not use the this operator with these members.

Let’s look into the code example to understand why we are saying that the compiler implicitly uses this pointer and when it is handy.

#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;
}

The above code example defines a Person class with two different member functions to print the private data member Name. The first statement in the main() function generates a Person object and passes Alexa as an argument to the constructor.

Now, this->Name in the constructor helps the compiler distinguish between local parameter Name and private data member Name.

The subsequent code of main() calls printName() and printName1() through P (i.e., P becomes the caller object).

Output:

Alexa
Alexa

The output of both the functions is the same. This is because the compiler implicitly precedes this-> with Name in the PrintName1().

Difference Between this and *this in C++

So far, we are clear with the concept that this is a pointer to an object. For an obj of type Person, this is of type Person*.

Another thing to remember is that the this pointer is always an rvalue, which can not be modified. However, *this de-references the this pointer.

After going through a sufficient background, let’s look at an example code to understand the difference between this and *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;
}

This code snippet creates a Counter class with multiple methods to GetCount methods. The first method (i.e., GetCount_Pointer) returns the value of this pointer which is simply an address of the calling object.

The GetCount_Copy method returns *this while the return type is a Counter type object. So, this function will return a deep copy of the calling object which means modifying the returned copy will not affect the original copy.

The last GetCount_Reference method also returns *this, but the return type of this method is a reference object (i.e., Counter& ). Instead of creating a new deep copy, the compiler will return an alias or reference to the original object.

Any changes made through the alias will also be reflected in the original object.

Before discussing the main driver code, let’s look at the program’s output.

Output:

Sectio-A
2
Section-B
3
2
Section-B
3

Section-A of the main method first declares the C1 object of Counter type and increments its Count through IncreaseCount(). Later, it initializes a CounterPtr pointer with the address returned by C1.GetCount_Pointer().

Now, the CounterPtr will be pointing to C1. Therefore, calling the increment function through CounterPtr also modifies the C1.

C2 = C1.GetCount_Copy() in section-B deeply copies all the current contents of C1 to C2 as C1.GetCount_Copy() is replaced with a copy of C1. Therefore, increasing count for C1 is not effecting C2.

The section-C declares a reference variable to Counter type and initializes it with whatever is being returned by C1.GetCount_Reference(). As C1.GetCount_Reference() returns an alias of C1, therefore, CounterRef becomes another name for C1.

The statement Counter& CounterRef = C1.GetCount_Reference(); becomes logically equivalent to Counter& CounterRef = C1.

Muhammad Husnain avatar Muhammad Husnain avatar

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

Related Article - C++ Pointer