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 initialized, or any field in a parent class; conversely, (implicitly or explicitly) accessing any fields that haven’t yet been initialized, or any fields in a derived class, is unsafe (due to the derived class not yet being constructed, and thus its fields neither being initialized 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: CtorThisBaseis fully constructed by the time theCtorThisconstructor is entered. Therefore,sis in a valid state while initialisingi, and can thus be accessed.iis initialised beforej(this->i)is reached. Therefore,iis in a valid state while initialisingj, and can thus be accessed.jis initialised beforek(j)is reached. Therefore,jis in a valid state while initialisingk, and can thus be accessed.- In the bad constructor, for
ctd_bad: kis initialised afterj(this->k)is reached. Therefore,kis in an invalid state while initialisingj, and accessing it causes undefined behaviour.CtorThisDerivedis not constructed until afterCtorThisis constructed. Therefore,bis in an invalid state while initialisingk, and accessing it causes undefined behaviour.- The object
ctd_badis still aCtorThisuntil 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().