Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Toggle Open Guide Table of ContentsGuide Contents

Close Table of ContentsGuide Contents

Close Table of Contents

Who's <tt>this</tt>?

Last updated Jan 1, 2003.

The keyword this is often defined in some textbooks as "a pointer holding the address of its object". This definition used to be correct in the very early days of C++. Later, it changed. Learn what this really is, how it affects member functions and data members and how its definitions changed in the past 20 years.

Analyze this

C++ creators introduced the notion of this -- a pointer that’s automatically filled with the address of the current class object. this is passed as an implicit argument to every nonstatic member function, thereby enabling that function to access its object’s data members and member functions directly:

class Point
{
private:
 int _x, _y;
public:
 explicit Point(int x=0, int y=0) : _x(x), _y(y) {}
 void set(int x, int y);
};

Point::set() assigns new values to the data members x and y:

void Point:set(int x, int y);
{
 _x=x;
 _y=y;
 }

Under the hood, the compiler transforms these assignment expressions into:

//pseudo C++ code
this->_x=x;
this->_y=y;

Some programmers who prefer to be more explicit also use this idiom:

//alternative coding style
//perfectly legal though redundant
void Point::set(int x, int y)
{
 this->_x=x;
 this->_y=y;
}

This coding style certainly doesn’t harm but it’s redundant as the compiler automatically adds this-> to every member accessed inside a member function.

The introduction of this has another useful advantage. It ensures a unique signature (and a unique mangled name) for member functions bearing the same name of plain old C functions (POF). For example, if your program has the following declarations:

void set(int, int); //a global function
class Point
{
 int _x, _y;
public:
 explicit Point(int x=0, int y=0) : _x(x), _y(y) {}
 void set(int x, int y); 
};

The functions set() and Point::set() have unique signatures although they appear to have identical names and parameter lists. The Point::set() actually takes three parameters: the two int’s declared explicitly and an implicit this argument. This brings us to another related issue: what exactly is the type of this?

More about this

Until the late 1980s, this was a mere pointer of type X * const, where X is the type of the class in question. Thus, inside every nonstatic member function of Point (including the constructor) this evaluates as a const pointer to Point. The compiler conceptually transforms the declaration of Point::set() into the following pseudo code:

//pseudo code
void set(Point * const _this, int x, int y); //Point::set

The signature of the POF set() remains as declared by the programmer:

//pseudo code
void set(int x, int y); //no change

The C++ standard doesn’t specify the exact position of this in a member function’s argument list; some compilers insert it at the beginning while others place it at the end:

//pseudo code; alternative parameter ordering
void set(int x, int y, Point * const _this); 

The introduction of this allowed for three more types of member functions: virtual, const and volatile member functions. virtual member functions are usually implemented by accessing a vptr, which is a compiler-generated pointer to a table of virtual function addresses.

How does this make your code safer? In order to ensure that const member functions are called only for const objects, C++ passes a different type of this to every const member function: ’const X * const’ (as opposed the default X *const) that is, a const pointer to const X. Let’s look at a concrete example:

class Point
{
public:
 int getx() const; /const member function
};

The compiler transforms the prototype of the const member function getx() internally into:

//pseudo C++ code
int getx(const Point * const __this);

Detecting invalid attempts to modify the state of the object inside getx() are caught at compile-time:

int Pointe::getx() const 
{
 _x=0; //error, can’t modify a member of a const object
 return _x;
}

According to the C++ standard, calling a non-volatile member function for a volatile object is ill-formed. C++ uses a similar technique with volatile member functions to trap such ill-formed code at compile-time. When you declare a volatile member function, that functions gets an implicit this of the following type: ’volatile X * const’. That is, a const pointer to volatile X. Consider:

class Point
{
public:
 int gety() volatile; 
};

The compiler the member function gety() internally into:

//pseudo C++ code
int gety(volatile Point * const __this);

The compiler can now detect ill-formed calls:

void func(volatile Point & p)
{
 int n= p.getx(); //ill-formed; getx() isn’t volatile
 int m=pgety(); //OK
}

Finally, when a member function is declared const volatile, its this argument is of type ’const volatile X *const’, i.e., a const pointer to const volatile X. The underlying signature of such a member function is:

//pseudo C++ code
int getx(const volatile Point * const __this);

Reanalyze this

Early implementations of C++ treated this as a real pointer. Consequently, programmers were allowed to assign a new value to this. The "assignment to this" idiom was useful in pre-historic times as a means of allocating objects inside the constructor, suppressing a destructor’s invocation, and for imitating a static member function call. Needless to say, such hacks were highly dangerous. The definition of this was modified in the late 1980s to prevent such hacks. Today, this isn’t a pointer; it’s an "rvalue expression that returns the address of its object". You can think of this as an operator that returns the address of its object, very much like operator sizeof that returns the size of its argument. When used inside a const or volatile member function, the result of a this expression is cv-qualified accordingly.

static Member Functions

static member functions were introduced only in cfront 2.0, more than 4 years after the first commercial implementation of C++. Unlike ordinary member functions, static member functions don’t take an implicit this argument. This characteristic affects how static member functions are used:

  • They cannot be virtual, const, or volatile.
  • They cannot access nonstatic members of their class.
  • They may be invoked without an object of their class.

Summary

For most programmers, understanding how exactly C++ represents and enforces the semantics of virtual, const and volatile member functions isn’t highly important. After all, that is why we have a high-level programming language and clever compilers! However, a deeper observation of the inner-working of C++ is, from my experience, an excellent way to enhance one’s programming skills and produce cleaner, faster and safer code.

Suggested Reading

Inside the C++ Object Model by Stanley B. Lippman, is one of my all-time favorites. Chapter 4, "the semantics of functions" explains in details the semantics of this and its implementation. Although this book is now 12 years old, it’s still one of the best ways to master the inner-workings of C++.

The Annotated Reference Manual by Margaret Ellis and Bjarne Stroustrup is an exemplary textbook on the inner-workings of C++. The annotations and commentary discuss what is not included in the language, why certain features are defined as they are, and how one might implement particular features. Here again, in spite of this book’s age it’s still an indispensable resource of C++ knowledge and expertise.