C++ this Pointer
Every non-static member function in C++ secretly receives a hidden argument: a pointer to the object it was called on. That pointer is called this, and it is how a member function knows which object’s data to read or modify when many objects of the same class exist. Understanding this demystifies how member functions actually work under the hood, and it unlocks useful patterns like resolving naming conflicts, returning references to the current object, and comparing object identity.
Overview: How the this Pointer Works
When you write a class with member functions, it looks like the function body can just refer to member variables directly, as if by magic. In reality, the compiler rewrites every non-static member function to take an extra, invisible parameter: a pointer to the object the function is being called through. That invisible parameter is named this.
Consider a call like box.volume(). Behind the scenes, the compiler treats it roughly like a free function call volume(&box), where the hidden parameter inside volume is this, and this is set equal to &box. Every unqualified reference to a member variable or member function inside the body, such as length, is really shorthand for this->length. The compiler inserts that this-> for you automatically wherever a name resolves to a member.
Because this is a pointer, it has a concrete type: inside a member function of class Box, the type of this is Box* (a pointer to non-const Box). Inside a const member function, the type becomes const Box*, which is precisely why you cannot modify member variables inside a function marked const — the compiler would be modifying data through a pointer to a const object, which is illegal. There is no this pointer at all inside static member functions, because a static function is not called on any particular object; it belongs to the class, not to an instance, so there is nothing for this to point at.
this is a prvalue, not a variable you can reassign. You cannot write this = &otherObject; to make a member function operate on a different object; the pointer is set once, automatically, when the function starts executing, and it never changes during that call.
Syntax
You rarely need to write this explicitly, since the compiler inserts it implicitly for plain member access. But there are three common explicit forms:
this->memberName— explicitly access a member variable or function through the pointer, most often to disambiguate from a same-named parameter or local variable.*this— dereference the pointer to get the actual current object (an lvalue reference to it), typically used to return the object itself from a member function.this == &other— compare the address the pointer holds against the address of another object, typically to detect self-assignment or self-reference.
| Expression | Meaning |
|---|---|
this |
Pointer to the current object (type ClassName*, or const ClassName* in a const member function) |
*this |
The current object itself, as an lvalue |
this->member |
Access a member of the current object explicitly |
return *this; |
Return a reference to the current object, enabling method chaining |
Examples
Example 1: Resolving a Naming Conflict
The most common reason to write this explicitly is when a constructor or setter’s parameter has the exact same name as the member variable it should assign to. Without this->, the parameter name would simply shadow the member, and the assignment would do nothing useful.
#include <iostream>
using namespace std;
class Box {
public:
Box(double length, double width, double height) {
this->length = length;
this->width = width;
this->height = height;
}
double volume() {
return this->length * this->width * this->height;
}
private:
double length;
double width;
double height;
};
int main() {
Box box(2.0, 3.0, 4.0);
cout << "Volume: " << box.volume() << endl;
return 0;
}
Output:
Volume: 24
Inside the constructor, the parameter length shadows the member variable length. Writing this->length unambiguously refers to the member, while the bare length refers to the parameter. This is by far the most frequent real-world use of an explicit this.
Example 2: Method Chaining with *this
Returning *this by reference from a member function lets you chain multiple calls on the same object in a single expression, similar to how cout << a << b chains insertions.
#include <iostream>
#include <string>
using namespace std;
class TextBuilder {
public:
TextBuilder& append(const string& text) {
content += text;
return *this;
}
TextBuilder& appendLine(const string& text) {
content += text + "\n";
return *this;
}
void print() const {
cout << content;
}
private:
string content;
};
int main() {
TextBuilder builder;
builder.append("Hello, ").append("World!").appendLine("").append("This is chained.");
builder.print();
cout << endl;
return 0;
}
Output:
Hello, World!
This is chained.
Each call to append or appendLine modifies content and then returns *this, which is a reference to the very same builder object. That let’s the next .append(...) be called directly on the result, chaining four calls into a single statement instead of writing four separate lines. This pattern, often called a fluent interface, is used heavily in libraries such as string-stream classes and builder-pattern APIs.
Example 3: Detecting Self-Reference with this
Because this holds an address, comparing it against the address of another object is a reliable way to detect when a function is being asked to operate on itself — a check that matters for things like assignment operators and, here, a bank transfer that should refuse to move money from an account to itself.
#include <iostream>
#include <string>
using namespace std;
class Account {
public:
Account(string owner, double balance) : owner(owner), balance(balance) {}
void transferTo(Account& other, double amount) {
if (this == &other) {
cout << "Cannot transfer to the same account." << endl;
return;
}
if (amount > balance) {
cout << "Insufficient funds." << endl;
return;
}
this->balance -= amount;
other.balance += amount;
cout << owner << " transferred " << amount << " to " << other.owner << endl;
}
void showBalance() const {
cout << owner << "'s balance: " << balance << endl;
}
private:
string owner;
double balance;
};
int main() {
Account alice("Alice", 500.0);
Account bob("Bob", 200.0);
alice.transferTo(bob, 150.0);
alice.transferTo(alice, 50.0);
alice.showBalance();
bob.showBalance();
return 0;
}
Output:
Alice transferred 150 to Bob
Cannot transfer to the same account.
Alice's balance: 350
Bob's balance: 350
The first transfer moves 150 from Alice to Bob, leaving Alice with 350 and Bob with 350. The second call passes alice as both the object the method is called on and the argument, so inside transferTo, this and &other hold the exact same address; the comparison catches that and the transfer is rejected before any balance is touched.
Under the Hood: Step by Step
When the statement alice.transferTo(bob, 150.0); executes, the following happens conceptually:
- The compiler resolves
transferTotoAccount::transferTobased on the static type ofalice. - It computes
&aliceand passes it as the hidden first argument, which becomesthisinside the function body. - The explicit arguments
bob(bound to the reference parameterother) and150.0(bound toamount) are passed as usual. - Inside the function, every bare reference to
balanceorowneris expanded by the compiler tothis->balanceandthis->owner, so the statementthis->balance -= amount;modifies Alice’s data specifically, not Bob’s. - When the function returns, the hidden
thisargument simply goes out of scope; nothing about the object itself is affected by the pointer’s lifetime.
This is also why calling a member function through a null pointer, such as Account* p = nullptr; p->showBalance();, compiles fine but produces undefined behavior at runtime the moment the function body dereferences this — the hidden pointer argument is simply null, and no member access through it is safe.
Common Mistakes
Mistake 1: Using this inside a static member function. Static member functions do not operate on any specific object, so there is no this to use. Trying to reference it is a compile error.
class Counter {
public:
static int getCount() {
return this->count; // error: 'this' is unavailable in a static member function
}
private:
static int count;
};
The fix is simply to drop this->, since a static function only ever accesses static members, which do not belong to any single instance:
class Counter {
public:
static int getCount() {
return count;
}
private:
static int count;
};
Mistake 2: Using dot notation instead of the arrow operator. Since this is a pointer, not an object, member access through it requires ->, not .. Writing this.x is a compile error because the dot operator does not apply to pointers.
class Point {
public:
void show() {
cout << this.x << ", " << this.y << endl; // error: this is a pointer, not an object
}
private:
int x = 0;
int y = 0;
};
Either dereference it first, or use the arrow operator, which is the idiomatic choice:
class Point {
public:
void show() {
cout << this->x << ", " << this->y << endl;
}
private:
int x = 0;
int y = 0;
};
Best Practices
- Only write
this->explicitly when it adds clarity, most commonly when a parameter name shadows a member variable; otherwise let the compiler insert it implicitly to keep code less cluttered. - Return
*thisby reference (not by value) from setter-style methods when you want to support method chaining, so no unnecessary copy of the object is made. - Use
this == &otherto guard against self-assignment inside a custom copy assignment operator, where copying an object’s resources onto itself can otherwise corrupt or double-free data. - Remember that a
constmember function receivesconst ClassName* this, so if you need to modify a member even inside a logically-const function, that member must be declaredmutable. - Never store a raw
thispointer for later use (for example in a callback or another container) without ensuring the object will still be alive when it is used; a danglingthisis just as dangerous as any other dangling pointer.
Practice Exercises
Exercise 1: Write a class Rectangle with a constructor that takes parameters named width and height, identical to its private member names. Use this-> to assign them correctly, and add an area() method that returns the product.
Exercise 2: Write a class Logger with methods info(const string& msg), warn(const string& msg), and print(), where each of the first two methods appends a formatted line to an internal string and returns *this, so you can chain calls like logger.info("started").warn("low memory").print();.
Exercise 3: Write a class Node with a method bool isSame(const Node& other) const that uses this to return true only when other refers to the very same object (not just an object with equal data). Test it by comparing a Node against itself and against a separate Node with identical data.
Summary
thisis a hidden pointer, implicitly passed to every non-static member function, that points to the object the function was called on.- Its type is
ClassName*in ordinary member functions andconst ClassName*inconstmember functions; there is nothisat all instaticmember functions. - Unqualified member names inside a member function are automatically expanded by the compiler to
this->member. - Writing
this->explicitly is most useful to disambiguate a member from a same-named parameter or local variable. return *this;returns the current object by reference, which is the basis of method chaining and fluent interfaces.- Comparing
this == &otherdetects when a function is being asked to operate on itself, which matters for self-assignment checks and similar guards.
