this Pointer
suggest changeAll non-static member functions have a hidden parameter, a pointer to an instance of the class, named this
; this parameter is silently inserted at the beginning of the parameter list, and handled entirely by the compiler. When a member of the class is accessed inside a member function, it is silently accessed through this
; this allows the compiler to use a single non-static member function for all instances, and allows a member function to call other member functions polymorphically.
struct ThisPointer {
int i;
ThisPointer(int ii);
virtual void func();
int get_i() const;
void set_i(int ii);
};
ThisPointer::ThisPointer(int ii) : i(ii) {}
// Compiler rewrites as:
ThisPointer::ThisPointer(int ii) : this->i(ii) {}
// Constructor is responsible for turning allocated memory into 'this'.
// As the constructor is responsible for creating the object, 'this' will not be "fully"
// valid until the instance is fully constructed.
/* virtual */ void ThisPointer::func() {
if (some_external_condition) {
set_i(182);
} else {
i = 218;
}
}
// Compiler rewrites as:
/* virtual */ void ThisPointer::func(ThisPointer* this) {
if (some_external_condition) {
this->set_i(182);
} else {
this->i = 218;
}
}
int ThisPointer::get_i() const { return i; }
// Compiler rewrites as:
int ThisPointer::get_i(const ThisPointer* this) { return this->i; }
void ThisPointer::set_i(int ii) { i = ii; }
// Compiler rewrites as:
void ThisPointer::set_i(ThisPointer* this, int ii) { this->i = ii; }
In a constructor, this
can safely be used to (implicitly or explicitly) access any field that has already been initialised, or any field in a parent class; conversely, (implicitly or explicitly) accessing any fields that haven’t yet been initialised, or any fields in a derived class, is unsafe (due to the derived class not yet being constructed, and thus its fields neither being initialised nor existing). It is also unsafe to call virtual member functions through this
in the constructor, as any derived class functions will not be considered (due to the derived class not yet being constructed, and thus its constructor not yet updating the vtable).
Also note that while in a constructor, the type of the object is the type which that constructor constructs. This holds true even if the object is declared as a derived type. For example, in the below example, ctd_good
and ctd_bad
are type CtorThisBase
inside CtorThisBase()
, and type CtorThis
inside CtorThis()
, even though their canonical type is CtorThisDerived
. As the more-derived classes are constructed around the base class, the instance gradually goes through the class hierarchy until it is a fully-constructed instance of its intended type.
class CtorThisBase {
short s;
public:
CtorThisBase() : s(516) {}
};
class CtorThis : public CtorThisBase {
int i, j, k;
public:
// Good constructor.
CtorThis() : i(s + 42), j(this->i), k(j) {}
// Bad constructor.
CtorThis(int ii) : i(ii), j(this->k), k(b ? 51 : -51) {
virt_func();
}
virtual void virt_func() { i += 2; }
};
class CtorThisDerived : public CtorThis {
bool b;
public:
CtorThisDerived() : b(true) {}
CtorThisDerived(int ii) : CtorThis(ii), b(false) {}
void virt_func() override { k += (2 * i); }
};
// ...
CtorThisDerived ctd_good;
CtorThisDerived ctd_bad(3);
With these classes and member functions:
- In the good constructor, for
ctd_good
: CtorThisBase
is fully constructed by the time theCtorThis
constructor is entered. Therefore,s
is in a valid state while initialisingi
, and can thus be accessed.i
is initialised beforej(this->i)
is reached. Therefore,i
is in a valid state while initialisingj
, and can thus be accessed.j
is initialised beforek(j)
is reached. Therefore,j
is in a valid state while initialisingk
, and can thus be accessed.- In the bad constructor, for
ctd_bad
: k
is initialised afterj(this->k)
is reached. Therefore,k
is in an invalid state while initialisingj
, and accessing it causes undefined behaviour.CtorThisDerived
is not constructed until afterCtorThis
is constructed. Therefore,b
is in an invalid state while initialisingk
, and accessing it causes undefined behaviour.- The object
ctd_bad
is still aCtorThis
until it leavesCtorThis()
, and will not be updated to useCtorThisDerived
’s vtable untilCtorThisDerived()
. Therefore,virt_func()
will callCtorThis::virt_func()
, regardless of whether it is intended to call that orCtorThisDerived::virt_func()
.