C++ Tutorial - The this Pointer - 2020
When I started leaning C++, one of the big questions was, - What the heck is this this?
As it turned out this is a special kind of pointer.
The this pointer stores the address of the class instance, to enable pointer access of the members to the member functions of the class. There is a secret not obvious to the new programmers - this pointer holds the key to the question:
How does C++ know which object it was called on?
The answer is that C++ has a hidden pointer named this!
Suppose that we create an object named objA of class A, and class A has a nonstatic member function void f(). If you call the function objA.f(), the keyword this in the body of f() stores the address of objA. The type of the this pointer for a member function of a class type A, is A* const.
So, when we call f(), we are actually calling f(&objA;). Since C++ converts the function call, it also needs to convert the function itself:
void A::f() { }
converted internally to:
void A::f(A* const this);
C++ has added a new parameter to the function. The added parameter is a pointer to the class object the class function is working with. It is always named this. The this pointer is a hidden pointer inside every class member function that points to the class object. As a result, an object's this pointer is not part of the object itself; it is not reflected in the result of a sizeof statement on the object. Instead, when a nonstatic member function is called for an object, the address of the object is passed by the compiler as a hidden argument to the function.
Let's take a look at a specific example. It's simple calculator code for something like Amazon.com's Cart and CheckOut.
#include <iostream> class Cart { private: int total; public: Cart(int item){ this->total = item; } Cart& addItem(int itemPrice) { total += itemPrice; return *this; } void checkOut() { std::cout << "total: " << total << " $\n"; } }; int main() { Cart myCart(0); myCart.addItem(100); myCart.checkOut(); }
In the code, when we call
myCart.addItem(100);
we are actually calling a function converted by compiler:
myCart.addItem(&myCart;, 100);
The function itself should be converted from
Cart& addItem(int itemPrice) { total += itemPrice; return *this; }to
Cart& addItem(myCart* const this, int itemPrice) { this->total += itemPrice; return *this; }
Note that the following expressions are equivalent:
this->total; (*this).total;
The fact that the return type is a reference means that the returned object is the invoking object itself (myCart) rather than a copy passed by the return mechanism.
In the main(), we did not utilize the returned object. If we modify the main a little (chain of addItem), we can add as many items we want. Here is a new code.
#include <iostream> class Cart { private: int total; public: Cart(int item){ this->total = item; } Cart& addItem(int itemPrice) { total += itemPrice; return *this; } void checkOut() { std::cout << "total: " << total << " $\n"; } }; int main() { Cart myCart(0); myCart.addItem(100).addItem(200).addItem(300); myCart.checkOut(); }
Output is simple:
total: 600 $
It's worth noting that this is a const pointer: we can change the value of the object it points to, but we cannot make it point to something else! So, we cannot declare the this pointer or make assignments to it.
If the member function is declared with the const qualifier, the type of the this pointer for that member function for class A, is const A* const.
A const this pointer can be used only with const member functions. Data members of the class will be constant within that function. The function is still able to change the value, but requires a const_cast to do so:
void A::f() const{ member = 2010: // illegal const_cast <int&> (member) =2010; // a bad practice but legal }
A better technique would be to declare member mutable. If the member function is declared with the volatile qualifier, the type of the this pointer for that member function for class A is volatile A* const. For example, the compiler will not allow the following:
class A { int n; int f() const { return n++; } };
The compiler will not allow the statement n++ in the body of function f(). In the function f(), the this pointer is of type A* const. The function f() is trying to modify part of the object to which this points.
The this pointer is also used to guard against self-reference:
if (&Object; != this) { // do not execute in cases of self-reference
Depending on whether they are declared as static, class member functions are treated differently. Since nonstatic functions have an implicit argument that supplies the this pointer, nonstatic functions are considered to have one more argument than static functions; otherwise, they are declared identically.
These nonstatic member functions require that the implied this pointer match the object type through which the function is being called, or, for overloaded operators, they require that the first argument match the object on which the operator is being applied.
Unlike other arguments in overloaded functions, no temporary objects are introduced and no conversions are attempted when trying to match the this pointer argument.
When the -> member-selection operator is used to access a member function, the this pointer argument has a type of class-name * const. If the members are declared as const or volatile, the types are const class-name* const and volatile class-name * const, respectively.
The . member-selection operator works exactly the same way, except that an implicit & (address-of) operator is prefixed to the object name.
If we write the following code:
obj.name
The compiler internally treats it like this:
(&obj;)->name
Are we able to use this inside the constructor?
Quick answer is yes. We are asking this question because we think at the time of using this pointer in a constructor, the object is not fully formed yet. However, we can use this in the constructor (in the {body} and even in the initialization list) if we are careful enough.
Here is something that always works: the {body} of a constructor (or a function called from the constructor) can reliably access the data members declared in a base class and/or the data members declared in the constructor's own class. This is because all those data members are guaranteed to have been fully constructed by the time the constructor's {body} starts executing.But things get complicated when we have to deal with virtual method.........
Deleting this?
No creative mind would do it. Only the destructive mind would try.
Anyway, I think it won't work. Even in the destructor. It may be like deleting null pointer. I tried with my Visual Studio, I couldn't get it done without a crash.
The fundamental issue is how we can figure it out where this is on the heap memory since we can use delete only on an object which is on the heap (free store) area.
In his book, "More Effective C++ 35 New Ways ...", Scott Meyers concludes, "After all, if we could tell when an address is on the heap, we could surely tell when an address is not on the heap. But we can't, so we can't. Oh Well.
Ph.D. / Golden Gate Ave, San Francisco / Seoul National Univ / Carnegie Mellon / UC Berkeley / DevOps / Deep Learning / Visualization