Ch13.7: The this Pointer
1. Every Member Function Quietly Receives the Object
When you write c.add(2), the compiler has to tell
add which counter to add to. It does that by passing the object as
an extra argument — the first one, not visible in the parameter list, and
reachable inside the function through the keyword this:
c.add(2); // what you write
// what the member function actually receives:
// (counter *this, ::std::int_least32_t n)
this is a pointer to that object. So
this->value_ and (*this).value_ and plain
value_ are three spellings of the same member. You rarely need to
write it; the three cases where you do are below.
2. Case 1: A Parameter Hides a Member
If a parameter has the same name as a data member, the parameter wins and the
member disappears from view. this-> brings it back:
counter &set(::std::int_least32_t value) noexcept
{
this->value_ = value; // member on the left, parameter on the right
return *this;
}
Many people avoid the question entirely by naming the parameter differently
(n, or new_value) — that works too, and gives
shorter names. Either way, this-> is the way to be explicit when
the two names collide.
3. Case 2: Returning the Object You Were Called On
*this is the object itself, not a pointer to it. Returning a
reference to it lets the caller chain calls on the same object, because every
call in the chain is a member function on the object the previous call
returned:
#include <fast_io.h>
#include <cstdint>
using namespace ::fast_io::io;
class counter
{
public:
explicit counter(::std::int_least32_t value) noexcept : value_{value}
{
}
// `value` hides the member, so the member needs this-> .
counter &set(::std::int_least32_t value) noexcept
{
this->value_ = value;
return *this;
}
counter &add(::std::int_least32_t n) noexcept
{
value_ += n;
return *this; // the object we were called on
}
::std::int_least32_t value() const noexcept
{
return value_;
}
bool same_as(counter const &other) const noexcept
{
return this == &other;
}
private:
::std::int_least32_t value_{};
};
int main()
{
counter c{0};
c.add(1).add(2).add(3); // chaining: each call returns *this
println("after chaining: ", c.value());
c.set(10);
println("after set(10): ", c.value());
counter d{10};
println("c.same_as(d) = ", c.same_as(d));
println("c.same_as(c) = ", c.same_as(c));
}
after chaining: 6
after set(10): 10
c.same_as(d) = 0
c.same_as(c) = 1
The return type is counter & — a reference to the object
itself, not a pointer and not a separate value, so c.add(1).add(2) reads like
c.add(1); c.add(2); with the object written once. If the chain may
be called on a const object, use counter const &
and a const member function
(Ch13.3).
4. Case 3: Do You Mean This Object?
A member function can compare the object it was called on against another one.
this is the address of the first, &other the
address of the second, so this == &other asks whether they are
the same object — not whether their contents are equal:
bool same_as(counter const &other) const noexcept
{
return this == &other; // one object, addressed twice?
}
In c.same_as(d) the addresses differ and the answer is
0; in c.same_as(c) both names refer to one object and
the answer is 1. Comparing the contents would be a different
function: return value_ == other.value_;. Both are useful; make
sure you know which question you asked.
5. In a const Member Function, this Is const
This is the mechanism behind Chapter 13.3. The keyword this follows
the const of the member function:
::std::int_least32_t value() const noexcept // inside: this is counter const *
{
return value_; // so value_ is const here too
}
A const member function therefore cannot assign to a member, and
cannot call a non-const member function — the object it holds
is read-only, and every member reached through it is read-only too.
this does not exist in a static member function
(Ch13.9). A static member
function is not called on an object, so there is no object to point at:
error: 'this' is unavailable for static member functions
And of course it does not exist in a free function either:
error: invalid use of 'this' in non-member function
this. this->value_
where plain value_ means the same thing adds noise that readers
have to filter. Write it when it disambiguates (Case 1), when it is the value
you need (Cases 2 and 3), or when a code editor’s syntax colouring is the
only thing telling your members apart — which no reader can see.