this Pointer

Hi everyone ✋

In the previous post we learned how to reach into an object through a pointer using the arrow operator ->. We pictured an object as a house 🏠, its members as rooms, and a pointer as the address slip that gets us there.

Today we turn that idea inward and ask a delightful little question ─ can an object hold a pointer to itself? 🤔

If we haven’t read the earlier posts in this series, it’s worth going through them first ─ today builds directly on the arrow operator post and borrows an idea or two from const pointers.

Today’s topic is the this pointer ─ a hidden little pointer that every object secretly carries to itself, quietly working behind the scenes inside every member function we write. Most of the time we never even see it. But once we know it’s there, a lot of everyday C++ suddenly makes perfect sense 😉

Let’s take a deep dive 🦴

So what is this? 🤔

Here’s a question you may have wondered about without realising it ─ when we call s.display() and the function inside prints marks, how does it know it means s‘s marks, and not some other student’s?

The answer is this. Every time a non-static member function runs, C++ quietly slips it a hidden pointer called this, holding the address of the exact object the function was called on. Call alice.display() and inside that call this points at alice. Call bob.display() and this points at bob. Same code, different this ─ that’s how one function serves every object.

So this is simply “a pointer to the object I’m currently working on.” Because it’s a pointer, we reach through it with the arrow -> we just learned ─ this->marks means “the marks of the object I’m working on.” Let’s give it a picture.

My little reference ─ the “this house” card by the door 🏠

Let’s keep our house running. Every object is a house, and its members are the rooms inside.

Now imagine that, tucked by the front door of every house, there’s a small card that reads “the address of THIS house.” That card is this. No matter which room ─ which member function ─ we’re standing in, we can glance at that card and instantly know the address of the whole house we’re inside.

When a member function simply says marks, it’s quietly reading that card first and going this->marks“the marks room of this house.” The card is how the house refers to itself. And just like a person saying “me” or “myself,” this is the object’s way of pointing back and saying “this one, right here.”

Keep that little card by the door in mind ─ every use of this is just the house reading its own address 😉

this is always there ─ working invisibly 👻

Here’s the first surprise ─ we’ve been using this in every single member function already, without typing it. When we write a bare member name inside a method, the compiler silently puts this-> in front of it. These two lines mean exactly the same thing ─

#include <iostream>
#include <string>
using namespace std;

class Student
{
public:
    string name;
    int    marks;

    void display()
    {
        // these two lines are identical ─ 'this->' is implied on the first
        cout << name       << " scored " << marks       << endl;
        cout << this->name << " scored " << this->marks << endl;
    }
};

int main()
{
    Student s;
    s.name  = "Imran";
    s.marks = 95;

    s.display();

    return 0;
}

The output will be ─

Imran scored 95
Imran scored 95

Both lines print the same thing, because name is this->name. The this pointer has been doing its quiet work all along ─ we just never had to name it. So most of the time, we happily leave it invisible. The interesting part is the handful of moments when writing this out loud actually earns its keep 👇

When we actually write this ─ #1, name clashes ✍️

This is the classic reason to reach for this. Picture a setter or a constructor whose parameter has the same name as the member it’s meant to fill ─ a very common, very natural thing to do ─

#include <iostream>
#include <string>
using namespace std;

class Student
{
    string name;
    int    marks;

public:
    void set(string name, int marks)
    {
        this->name  = name;    // member 'name'  = parameter 'name'
        this->marks = marks;   // member 'marks' = parameter 'marks'
    }

    void display()
    {
        cout << name << " scored " << marks << endl;
    }
};

int main()
{
    Student s;
    s.set("Ada", 100);
    s.display();

    return 0;
}

The output will be ─

Ada scored 100

Inside set, the plain name name refers to the parameter (the local one wins). So how do we reach the member with the same name? We read the card ─ this->name says “the name room of this house,” clearly separating it from the loose name sitting on the table. So whenever you have a parameter shadowing a member, reach for this-> to point past it to the object’s own field. Clean, readable, and unambiguous 🥳

#2 ─ method chaining with return *this 🔗

Here’s a lovely one. If this is a pointer to our object, then *this ─ dereferencing it ─ is the object itself. And that lets us do something elegant ─ a member function can hand the whole object back to the caller, so calls can be strung together in a chain.

#include <iostream>
#include <string>
using namespace std;

class Student
{
    string name;
    int    marks;

public:
    Student& setName(string n)  { this->name  = n; return *this; }
    Student& setMarks(int m)    { this->marks = m; return *this; }

    void display() { cout << name << " scored " << marks << endl; }
};

int main()
{
    Student s;

    s.setName("Grace").setMarks(99).display();   // one smooth chain

    return 0;
}

The output will be ─

Grace scored 99

Look at that middle line in mainsetName sets the name and then hands back the very same object with return *this, so .setMarks(99) can act on it, which hands it back again for .display(). Each call returns “myself,” so the next call has something to grab onto. Notice the return type is Student& (a reference) ─ we hand back the real object, not a copy, which is exactly what lets the chain keep flowing. This pattern is everywhere in real C++ ─ it’s even how cout << a << b << c chains under the hood. Let’s watch it solve a real problem 🥳

A real problem ─ the fluent builder 🍕

That chaining trick isn’t just a neat toy ─ it’s the backbone of the builder pattern, one of the most readable ways to construct an object step by step. Let’s build a pizza order and watch return *this turn a pile of setters into a single, sentence-like line.

#include <iostream>
#include <string>
using namespace std;

class Pizza
{
    string size      = "medium";
    bool   cheese    = false;
    bool   pepperoni = false;

public:
    Pizza& setSize(string s)  { this->size      = s;    return *this; }
    Pizza& addCheese()        { this->cheese    = true; return *this; }
    Pizza& addPepperoni()     { this->pepperoni = true; return *this; }

    void print()
    {
        cout << size << " pizza";
        if (cheese)    cout << " + cheese";
        if (pepperoni) cout << " + pepperoni";
        cout << endl;
    }
};

int main()
{
    Pizza order;

    order.setSize("large").addCheese().addPepperoni().print();

    return 0;
}

The output will be ─

large pizza + cheese + pepperoni

Read that one line in main almost like English ─ “take the order, set size large, add cheese, add pepperoni, print.” Every setter tweaks one thing and then returns *this, so the next setter has the same object to work on. Without this, we’d be stuck writing order.setSize("large"); order.addCheese(); order.addPepperoni(); on separate lines, repeating order every time. This is exactly how real libraries build strings, database queries, and HTTP requests ─ and it all rests on that quiet return *this 🥳

#3 ─ the self-check with if (this == &other) 🪞

One more real-world use, and it shows off this as an address. Sometimes an object needs to ask ─ “is this other object actually me?” The most famous case is the copy-assignment operator, where we guard against a student being assigned to itself ─

#include <iostream>
#include <string>
using namespace std;

class Student
{
    string name;
    int    marks;

public:
    Student(string n = "", int m = 0) : name(n), marks(m) {}

    Student& operator=(const Student& other)
    {
        if (this == &other)     // are we assigning to ourselves?
            return *this;        // yes ─ nothing to copy, just leave

        name  = other.name;
        marks = other.marks;
        return *this;
    }

    void display() { cout << name << " scored " << marks << endl; }
};

int main()
{
    Student a("Ada", 100), b;

    b = a;      // normal copy
    b.display();

    b = b;      // self-assignment ─ safely handled
    b.display();

    return 0;
}

The output will be ─

Ada scored 100
Ada scored 100

The key line is if (this == &other). Since this is the address of our object and &other is the address of the one being assigned, comparing them asks “are these the same house?” If so, there’s nothing to do. Don’t worry if operator overloading feels new ─ the point here is simply that this, being a genuine address, lets an object recognise itself among others 🪞. It’s the same trick behind avoiding accidental self-deletion and other “wait, is that me?” checks.

#4 ─ handing this to other code 🤝

So far we’ve read this and returned *this. But because this is a real pointer, an object can also hand itself to other code ─ passing this along so something else can work with “me.”

This shows up constantly in data structures, where an object needs to wire itself into a bigger picture. Here’s a linked-list node that splices a new node in right after itself, using this to mean “the node I am”

#include <iostream>
using namespace std;

struct Node
{
    int   data;
    Node *next = nullptr;

    void insertAfter(Node *n)     // splice n in right after THIS node
    {
        n->next    = this->next;  // n takes over wherever I currently point
        this->next = n;           // and now I point at n
    }
};

int main()
{
    Node a{ 10 }, b{ 30 }, c{ 20 };

    a.insertAfter(&b);   // list becomes: a -> b
    a.insertAfter(&c);   // list becomes: a -> c -> b

    for (Node *p = &a; p != nullptr; p = p->next)
        cout << p->data << " ";
    cout << endl;

    return 0;
}

The output will be ─

10 20 30 

Inside insertAfter, this->next and this let the node rewire the list from its own point of view ─ “point the newcomer where I point, then point me at the newcomer.” The same idea powers self-registration patterns, where an object hands this to a manager or event system to say “here I am, keep track of me.”

But this power comes with a sharp edge ⚠️ ─ if we hand this to something that stores it, and our object later dies, that stored pointer becomes a dangling pointer (the exact hazard from the arrow operator post). Whoever we handed this to must never outlive the object itself. So share this freely ─ just make sure it never points at a house that’s already been torn down ☠️.

The type of this ─ and how const changes it 🔒

Now for a detail that ties this whole series together beautifully. What type is this, exactly?

Inside a normal member function of a class Student, this has the type Student * const ─ a constant pointer to a Student. If we think back to the const pointers post, that’s our bolted-down remote 🔩 ─ we can change the object it points at (the buttons work), but we can never re-aim this at a different object. That’s why this = &somethingElse; is forbidden ─ the card by the door always holds our own address, and nothing can change that.

And here’s where it clicks with the arrow operator post ─ remember how a const member function makes the object read-only? Now we can see exactly why. In a const member function, this becomes const Student * const ─ a constant pointer to a constant object 🔩🔇. Both locks are on. So we can read our members but not change them ─

void display() const
{
    cout << name << " scored " << marks << endl;   // ✅ reading is fine
    // marks = 0;   // ❌ ERROR ─ 'this' is a pointer-to-const here
}

If we uncomment that line, the compiler stops us ─

error: assignment of member 'Student::marks' in read-only object

The “read-only object” in that message is the object this points at. A const member function simply hands us a locked-down this, and every member inherits the lock. Two posts, one idea, clicking together 🥳

this has its limits ─ static functions and beyond 🚫

A couple of boundaries are worth knowing.

First ─ static member functions have no this at all. A static function belongs to the class, not to any one object, so there’s no particular house for it to point at. That means it can’t touch non-static members either ─

static void info()
{
    // cout << marks;   // ❌ ERROR ─ no object, so no 'this'
    cout << "This is the Student class" << endl;
}

Trying to use a member there gives ─

error: invalid use of member 'Student::marks' in static member function

Second ─ as we saw, this can never be reassigned, because it’s a const pointer. It quietly appears when a member function starts, points faithfully at our object for the whole call, and vanishes when the function ends. We read it, we pass it, we dereference it ─ but we never change it.

An advanced curiosity ─ delete this ⚠️

Here’s a strange one that surprises everybody the first time they see it ─ an object can, quite literally, delete itself

class Widget
{
public:
    void destroy()
    {
        delete this;   // the object frees its own memory 😱
    }
};

It looks like science fiction, but it’s real, and a few self-managing designs (like some reference-counting objects that delete themselves when their count hits zero) rely on it. That said, it’s advanced and easy to get wrong, so it comes with strict rules ─

  • The object must have been created with new ─ calling delete this on a stack object is catastrophic, because we can only delete what we newed.
  • After delete this, we must never touch the object again ─ not a member, not this, nothing. The house is gone; reading the card by the door is now reading rubble ☠️.
  • Whoever called us must know the object is gone and drop their pointer (set it to nullptr), or they’re left holding a dangling pointer.

For everyday code, we almost never need delete this ─ and honestly, if we find ourselves reaching for it, smart pointers usually solve the same problem far more safely. It’s worth recognising if we ever meet it in the wild, but it’s firmly a “handle with extreme care” tool 🧤.

Gotchas to keep us safe ⚠️

Let’s gather every tripwire in one place ─

  • this only exists in non-static member functions. Static functions and free functions have no this, because there’s no particular object to point at.
  • this is a pointer; *this is the object. Use this->member to reach a member, and *this when we need the whole object (like return *this;).
  • this is a const pointerStudent * const. We can’t re-aim it, so this = ...; is illegal.
  • In a const member function, this is pointer-to-constconst Student * const. That’s precisely why const methods can’t modify members.
  • Return *this by reference for chaining, not by value ─ returning a copy breaks the chain and quietly duplicates the object.
  • this == &other compares identity. It’s the standard self-assignment guard, and it works because this is a real address.
  • An object can hand out this, but whoever stores it must not outlive the object ─ a stored this to a destroyed object is a dangling pointer ☠️.
  • delete this is real but advanced. It’s only valid for heap objects, and the object (and every pointer to it) must never be used afterward. Prefer smart pointers.

Keep these in mind and this stays the friendly, invisible helper it was designed to be 🥳

So what should we remember? 🤔

Let’s wrap up the key takeaways ─

  • this is a hidden pointer that every non-static member function receives ─ it holds the address of the object the function was called on.
  • Picture the “this house” card by the door 🏠 ─ any room (member function) can read it to get the address of the whole house. It’s how an object refers to itself.
  • Every bare member access already uses this ─ writing marks inside a method is exactly this->marks. It works invisibly almost all the time.
  • We write this explicitly for a few real jobs ─ disambiguating a member from a same-named parameter (this->name = name;), method chaining (return *this;), self-identity checks (if (this == &other)), and handing the object to other code (passing this along).
  • *this is the object itself ─ dereferencing the pointer gives us the whole object, which is what makes return *this; and fluent chaining possible. It’s the engine behind the builder pattern and cout << a << b << c.
  • An object can even hand out this to wire itself into a structure or register itself ─ but a stored this must never outlive its object, or it dangles ☠️.
  • The type of this ties the series together ─ it’s a Student * const (a const pointer), and in a const method it’s const Student * const, which is why const methods can’t modify members.
  • static member functions have no this, this can never be reassigned, and delete this (an object deleting itself) exists but is strictly an advanced, heap-only, handle-with-care tool.

Once this clicks, member functions stop feeling like magic ─ every one of them quietly carries a little card that says “the address of this house,” and everything else follows from that 👀✨

Congratulations 🥳🥳🥳
We now understand the this pointer ─ the invisible self-reference at the heart of every object.

In the next post we’ll push this idea one step further ─ if an object can point to itself, can we take the address of an individual member? We’ll meet pointers to members ─ the curious Class::* syntax, and the .* and ->* operators that come with it 😉

Happy Coding 💻 🎵

Leave a Comment

Your email address will not be published. Required fields are marked *