AlgoMaster Logo

this Pointer

Medium Priority14 min readUpdated June 6, 2026
Listen to this chapter
Unlock Audio

Every non-static member function of a class has a hidden parameter: a pointer to the object the function was called on. That pointer is called this, and it can be used explicitly when the implicit version is not enough. This lesson covers what this actually is, when typing it is required, the chaining pattern that returns *this, the address-comparison technique used in copy and assignment, and a few sharp edges around null calls and delete this;.

What this Actually Is

Inside a member function, this is a built-in pointer that points to the instance the function was called on. The compiler generates it; the programmer does not declare it. A call like cart.addItem(...) effectively passes &cart to addItem as a hidden first argument, and inside the function that hidden argument is named this.

The value of this inside mouse.showAddress() is exactly &mouse. Inside keyboard.showAddress() it is exactly &keyboard. Same function, two different this pointers, because the call site picks which object is current.

The model:

The function body is the same machine code in both calls. The only difference is the hidden this argument, which is how the function knows which fields to read.

Inside the function, every unqualified mention of a member is implicitly this->member. When showAddress references name, the compiler treats it as this->name. Typing this-> directly is rarely required, but understanding that it is always there clears up a lot of confusion later.

When You Actually Need to Type this->

Most code never writes this-> because the implicit version is shorter and reads better. Two situations require it.

The first is name shadowing. When a constructor or setter takes a parameter with the same name as a member, the parameter wins inside the function body, and the member is hidden. this-> reaches past the parameter to the member.

Forgetting the this-> here is a silent bug. name = name; is legal C++: it assigns the parameter name to itself, leaving the member name empty. With -Wself-assign (on by default with -Wall in modern g++), a warning appears, but warnings are not errors. Naming the member name_ or m_name is a common style choice precisely to dodge this trap; using this-> is the other common fix.

The second situation is in templates that inherit from a class template. The lookup rules require this-> to find inherited members. That case appears in the templates chapter. For now, name shadowing in constructors and setters is the practical reason to type this->.

Writing this->name versus name is identical at runtime. The compiler resolves both to the same memory access. The choice is about disambiguating names for the compiler and for human readers.

Returning *this for Method Chaining

Sometimes several setters on the same object need to be called in one line. The pattern that enables this has each setter return a reference to the current object so the next call can be chained onto the result.

return *this; dereferences the this pointer, producing a reference to the current object. The return type Order& declares that the function gives back a reference, not a copy. Once those two are wired together, every setter call evaluates to the same Order instance, and the next .setX(...) keeps working on it.

The reason this pattern is called fluent setters is that the code reads like a small sentence: configure customer, then address, then item count, then total. It is a popular style in builder objects and configuration APIs.

Be careful about the return type. Writing Order setCustomer(...) instead of Order& setCustomer(...) returns a copy of the object instead of a reference. The chain still compiles, but every link operates on a fresh copy that is thrown away at the end of the expression, so the original o never changes. That is a silent bug.

Comparing Addresses with this

Some operations involve two objects of the same class, and the function needs to know whether they are actually the same object in memory. The standard technique is to compare the address of the other object against this.

&other == this asks: "Is the object passed in the same object the function was called on?" When the answer is yes, the function can skip the copy entirely, or take a shortcut. For a small struct with simple fields, self-copy is harmless: every field is copied onto itself. The check becomes important when the class manages a resource that gets released before being re-acquired, because a self-copy would release the resource and then try to copy from the released slot.

This same check shows up in copy-assignment operators, covered in the Operator Overloading lesson later. The basic idea is identical: detect self-assignment so the assignment logic does not corrupt the object.

The full mechanics of operator= belong to its own lesson. For now, the takeaway is that comparing this against the address of another object is how a member function detects "operating on myself", and that detection is sometimes critical for correctness.

The address comparison is a single integer compare. There is no measurable runtime cost. The reason to skip self-copy is correctness for resource-managing classes, not performance.

this in const Member Functions

When a member function is declared with const after the parameter list, the type of this changes. In a non-const member function of Product, this has type Product* const: a pointer that cannot be reassigned, pointing to a non-const object. In a const member function, this has type const Product* const: the object it points to is also const.

The practical consequence: inside a const member function, no member can be modified through this, and no non-const member function can be called on this.

Inside getStock(), the compiler treats this as const Product* const, so stock = 100; would be rejected with an error like assignment of member 'Product::stock' in read-only object from g++. That is the const-correctness machinery working as intended: a function that promises not to modify the object cannot break that promise, even by accident.

The full coverage of const member functions, including how they interact with const references and overloading, lives in the Encapsulation chapter. For this lesson, the point is that this carries the const-ness of the function, and that is why a const member function cannot write to fields.

this Is Never Null in a Valid Call

A call like obj.method() or ptr->method() on a valid object makes this inside the function point at that object. What happens with a member function call through a null pointer?

The line p->print() is undefined behavior in C++. The standard says calling a member function through a null pointer makes the entire program ill-defined. The compiler can assume any code reaching the inside of a non-static member function has a valid this, and it generates code based on that assumption.

The observed behavior on real hardware depends on the function. If print() only touches members, the access stock inside the function expands to this->stock, which dereferences a null pointer and typically crashes with a segmentation fault. On some platforms, calling a non-virtual member function that never reads or writes any member can appear to "work" because no actual dereference happens. That apparent success is a trap. The behavior is undefined, the compiler can optimize on that assumption, and the program is broken even when it looks fine.

The rule: never call a member function on a null pointer. When a pointer might be null, check it before calling anything on it.

The mirror of this rule is also useful: inside a non-static member function, do not write if (this == nullptr) to defend against null calls. The standard says that branch can never be reached in a well-formed program, and modern compilers will sometimes delete the check entirely as dead code. The right place for the null check is at the call site, not inside the function.

delete this; and Why It Is Dangerous

C++ allows a member function to write delete this;, which destroys the object the function was called on. It is legal syntax, and a few patterns rely on it, but the requirements are strict enough that it should be treated as a last-resort tool.

The output looks clean, but the requirements that make this code valid are easy to violate:

  1. The object must have been allocated with new (not on the stack, not as a member of another object, not statically). delete this; calls operator delete, which only works on new-allocated memory.
  2. After delete this;, the function must not touch any member of the object, including reading this itself. Every field is gone.
  3. After delete this; returns to the caller, no other code can use the original pointer. The caller usually sets its pointer to nullptr, and any other pointer that aliased the object becomes a dangling pointer.
  4. The object must not be referenced anywhere else in the program. A second pointer holding the same address now points at freed memory.

The stack-allocated Session in this example cannot be deleted, because delete only frees heap memory. When logout() runs delete this; against a stack address, the program corrupts the heap allocator's internal state and typically crashes immediately or much later, often in unrelated code, which makes the bug hard to debug.

The performance cost of delete this; is the same as any other delete: one call into the allocator. The real cost is the cognitive overhead and the risk. Almost every use case for delete this; is better served by smart pointers (std::unique_ptr, std::shared_ptr) or by having the owner of the object call delete from outside.

Legitimate uses do exist. Reference-counted objects sometimes call delete this; when the count drops to zero. Plug-in systems that hand objects across module boundaries sometimes use it because the caller cannot see the destructor directly. In day-to-day application code, especially when learning C++, skip delete this; entirely and let smart pointers manage lifetimes.

Quiz

this Pointer Quiz

10 quizzes