What Is the Hidden this Pointer and Member Function Chaining?

The this pointer is a hidden pointer that every non-static member function receives, pointing to the object the function was called on. Understanding this also enables member function chaining, a technique for calling multiple member functions on the same object in a single expression.

A common question beginners have when learning about classes is: "When I call a member function, how does C++ know which object's data to use?"

Consider this straightforward class that manages a bank account balance:

#include <iostream>

class BankAccount
{
private:
    double m_balance{};

public:
    BankAccount(double balance)
        : m_balance{ balance }
    {
    }

    double getBalance() const { return m_balance; }
    void deposit(double amount) { m_balance += amount; }

    void display() const { std::cout << m_balance; }
};

int main()
{
    BankAccount account{100.0};
    account.deposit(50.0);

    account.display();

    return 0;
}

This program outputs:

150

When we execute account.deposit(50.0);, C++ somehow knows that deposit() should modify account.m_balance specifically. The mechanism behind this is the this pointer.

The hidden this pointer

Inside every non-static member function, the keyword this is a const pointer that holds the memory address of the current object being operated on.

We usually don't need to explicitly use this, but we can if we want to:

#include <iostream>

class BankAccount
{
private:
    double m_balance{};

public:
    BankAccount(double balance)
        : m_balance{ balance }
    {
    }

    double getBalance() const { return m_balance; }
    void deposit(double amount) { m_balance += amount; }

    void display() const { std::cout << this->m_balance; } // explicitly use this pointer
};

int main()
{
    BankAccount account{ 100.0 };
    account.deposit(50.0);

    account.display();

    return 0;
}

This produces identical output:

150

Both versions of the display() function do exactly the same thing:

    void display() const { std::cout << m_balance; }       // implicit this
    void display() const { std::cout << this->m_balance; } // explicit this

The first version is shorthand for the second. During compilation, the compiler automatically adds this-> before any member name that refers to the current object's members.

Reminder
We use the arrow operator (`->`) to access members through a pointer. The expression `this->m_balance` is equivalent to `(*this).m_balance`.

How does this get set?

Let's examine this function call more closely:

    account.deposit(50.0);

Although it looks like deposit(50.0) only takes one argument, the compiler actually converts it to something like this:

    BankAccount::deposit(&account, 50.0); // account becomes a function argument!

This is now a regular function call where the object account is passed by address as the first argument.

But there's more. The member function definition must also be modified to accept this hidden parameter. Here's our original deposit() function:

    void deposit(double amount) { m_balance += amount; }

The compiler transforms it into something conceptually similar to:

    static void deposit(BankAccount* const this, double amount) { this->m_balance += amount; }

The function now has a new leftmost parameter called this, which is a const pointer. The m_balance member is rewritten as this->m_balance to use this pointer.

Advanced note: In this context, static means the function is not associated with an instance of the class, but is treated as a normal function within the class's scope.

Putting everything together:

  1. When we call account.deposit(50.0), the compiler actually calls BankAccount::deposit(&account, 50.0), passing account by address.
  2. The function has a hidden parameter this that receives the address of account.
  3. Member variables in deposit() are prefixed with this->, so this->m_balance actually resolves to account.m_balance.

The good news is this all happens automatically. You just need to remember that all non-static member functions have a this pointer referring to the object the function was called on.

Core Understanding

All non-static member functions have a this const pointer that holds the address of the object being operated on.

this always points to the current object

New programmers sometimes wonder how many this pointers exist. Each member function has one this pointer parameter that points to the current object. Consider:

int main()
{
    BankAccount checking{500.0};  // this = &checking inside the constructor
    BankAccount savings{1000.0};  // this = &savings inside the constructor
    checking.deposit(100.0);      // this = &checking inside deposit()
    savings.deposit(200.0);       // this = &savings inside deposit()

    return 0;
}

The this pointer alternately holds the address of checking or savings depending on which object's member function we're calling.

Since this is just a function parameter (not a member), it doesn't make your class objects consume more memory.

Explicitly referencing this

Most of the time, you won't need to explicitly use this. However, there are specific cases where it's useful:

First, if a member function parameter has the same name as a data member, you can disambiguate using this:

struct Counter
{
    int count{}; // no m_ prefix because this is a struct

    void setCount(int count)
    {
        this->count = count; // this->count is the member, count is the parameter
    }
};

In this example, the member is count and the parameter is also count. Within setCount(), the name count refers to the parameter (because it shadows the member). To access the member, we use this->count.

Some developers prefer explicitly adding this-> to all member access for clarity. We recommend avoiding this practice as it makes code less readable. Using the m_ prefix is a more concise way to distinguish members from local variables.

Returning *this

Sometimes it's useful for a member function to return the current object. This allows function chaining (also called method chaining), where multiple member functions can be called on the same object in one expression.

Consider this familiar example with std::cout:

std::cout << "Account balance: " << balance;

The compiler evaluates this as:

(std::cout << "Account balance: ") << balance;

First, operator<< prints "Account balance: " to the console. But since this is part of an expression, operator<< must return a value. If it returned void, we'd end up with:

void{} << balance;

This wouldn't make sense. Instead, operator<< returns the stream object (std::cout) that was passed in. After the first operation completes:

(std::cout) << balance;

This then prints the balance. By returning std::cout, we only need to specify it once and can chain together as many outputs as needed.

We can implement similar behavior in our own classes. Consider this calculator class:

class Calculator
{
private:
    int m_value{};

public:

    void add(int value) { m_value += value; }
    void subtract(int value) { m_value -= value; }
    void multiply(int value) { m_value *= value; }

    int getValue() const { return m_value; }
};

To perform multiple operations, we'd need to write:

#include <iostream>

int main()
{
    Calculator calc{};
    calc.add(10);      // returns void
    calc.subtract(3);  // returns void
    calc.multiply(2);  // returns void

    std::cout << calc.getValue() << '\n';

    return 0;
}

However, if we make each function return *this by reference, we can chain operations together:

class Calculator
{
private:
    int m_value{};

public:
    Calculator& add(int value) { m_value += value; return *this; }
    Calculator& subtract(int value) { m_value -= value; return *this; }
    Calculator& multiply(int value) { m_value *= value; return *this; }

    int getValue() const { return m_value; }
};

Now we can write:

#include <iostream>

int main()
{
    Calculator calc{};
    calc.add(10).subtract(3).multiply(2); // method chaining

    std::cout << calc.getValue() << '\n';

    return 0;
}

We've condensed three lines into one expression! Let's examine how this works.

First, calc.add(10) adds 10 to m_value. Then add() returns *this, which is a reference to calc, so calc is used in the next evaluation. Next, calc.subtract(3) executes, subtracts 3, and again returns calc. Finally, calc.multiply(2) multiplies by 2 and returns calc, which isn't used further.

Since each function modified calc during execution, m_value now contains ((0 + 10) - 3) * 2, which equals 14.

This is the most common explicit use of this and should be considered whenever chainable member functions make sense.

Because this always points to the current object, we don't need to check for null before dereferencing it.

Resetting a class to default state

If your class has a default constructor, you might want a way to reset an existing object to its default state.

As noted in previous lessons (14.12 -- Delegating constructors), constructors are only for initialization of new objects and should not be called directly, as this causes unexpected behavior.

The best way to reset a class to default state is to create a reset() member function that creates a new object using the default constructor, then assigns it to the current object:

    void reset()
    {
        *this = {}; // value initialize a new object and overwrite the current object
    }

Here's a complete example:

#include <iostream>

class Calculator
{
private:
    int m_value{};

public:
    Calculator& add(int value) { m_value += value; return *this; }
    Calculator& subtract(int value) { m_value -= value; return *this; }
    Calculator& multiply(int value) { m_value *= value; return *this; }

    int getValue() const { return m_value; }

    void reset() { *this = {}; }
};

int main()
{
    Calculator calc{};
    calc.add(10).subtract(3).multiply(2);

    std::cout << calc.getValue() << '\n'; // prints 14

    calc.reset();

    std::cout << calc.getValue() << '\n'; // prints 0

    return 0;
}

this and const objects

For non-const member functions, this is a const pointer to a non-const value (meaning this cannot be repointed, but the object it points to can be modified). For const member functions, this is a const pointer to a const value (the pointer cannot be repointed, and the object cannot be modified).

The errors from attempting to call a non-const member function on a const object can be cryptic:

error C2662: 'int Counter::getValue(void)': cannot convert 'this' pointer from 'const Counter' to 'Counter &'
error: passing 'const Counter' as 'this' argument discards qualifiers [-fpermissive]

When we call a non-const member function on a const object, the implicit this parameter is a const pointer to a non-const object. But the argument has type const pointer to a const object. Converting a pointer to a const object into a pointer to a non-const object requires discarding the const qualifier, which cannot be done implicitly. The compiler error reflects this conversion issue.

Why is this a pointer and not a reference?

Since this always points to the current object (and can never be null unless something causes undefined behavior), you might wonder why it's a pointer instead of a reference. The answer is simple: when this was added to C++, references didn't exist yet.

If this were added to C++ today, it would certainly be a reference. In more modern C++-like languages such as Java and C#, this is implemented as a reference.

Summary

The hidden this pointer: Every non-static member function has a hidden parameter called this - a const pointer that holds the address of the object the function was called on. The compiler automatically passes the object's address as the first argument when calling member functions.

Implicit member access: When accessing members inside a member function, the compiler implicitly adds this-> before member names. Writing m_balance is shorthand for this->m_balance. This happens automatically, so explicit use of this is rarely needed.

How this is set: When you call obj.function(), the compiler transforms it to Class::function(&obj), passing the object's address. The function receives this address through its hidden this parameter.

One this per function call: Each member function has one this parameter that points to whichever object the function was called on. The this pointer doesn't consume additional memory in your objects since it's just a function parameter.

When to use this explicitly: Use this-> to disambiguate when a parameter shadows a member (like this->count = count;), though using the m_ prefix is generally preferable. The main use case for this is returning the current object.

Returning *this for chaining: Return *this by reference to enable method chaining. This allows multiple member function calls on the same object in one expression: calc.add(10).subtract(3).multiply(2);. Each function modifies the object and returns a reference to it.

Resetting to default state: Use *this = {}; to reset an object to its default-constructed state. This value-initializes a new temporary object and assigns it to the current object.

this with const objects: For const member functions, this is a const pointer to a const object, preventing modification. For non-const member functions, this is a const pointer to a non-const object, allowing modification.

The this pointer is fundamental to how member functions work in C++, enabling objects to operate on their own data while supporting powerful patterns like method chaining and fluent interfaces.