- Overview
-
Table of Contents
- Special Member Functions: Constructors, Destructors, and the Assignment Operator
- Operator Overloading
- Memory Management
- Templates
- Namespaces
- Time and Date Library
- Streams
- Object-Oriented Programming and Design Principles
- The Standard Template Library (STL) and Generic Programming
- Exception Handling
- Runtime Type Information (RTTI)
- Signal Processing
- Creating Persistent Objects
- Bit Fields
- New Cast Operators
- Environment Variables
- Variadic Functions
- Pointers to Functions
- Function Objects
- Pointers to Members
- Lock Files
- Design Patterns
- Dynamic Linking
-
Tips and Techniques
- Using the Swap() Algorithm
- Using class stopwatch for Performance Measurements
- Extending <tt><iostream></tt> to Support User-Defined Types
- Using <tt>auto_ptr</tt> To Automate Memory Management
- Using <tt>auto_ptr</tt> To Automate Memory Management, Part II
- Using <tt>auto_ptr</tt> To Automate Memory Management, Part III
- Using <tt>enum</tt>s as Mnemonic Indexes
- Create Objects on Pre-Allocated Memory Using <tt>Placement-new</tt>
- Online Books: <tt>Placement-new</tt>
- Bitwise Operators
- Bitwise Operators II
- Who's <tt>this</tt>?
- A Reference Guide
- The Virtues of Multiple Inheritance
- Interfaces
- Multiple Inheritance: Construction and Destruction Order
- nothrow new
- POD Initialization
- Object Initialization
- <tt>const</tt> Declarations
- The Semantics of <tt>volatile</tt>
- <tt>inline</tt> Functions
- Project Organization Guidelines
- All About <tt>bool</tt>
- <tt>typedef</tt> Declarations
- State of the <tt>union</tt>
- Dynamic Cast Uses
- Integrating C and C++
- <tt>const</tt>-Correctness
- <tt>const</tt>-Correctness: Advanced Issues
- Sprucing Up Legacy Code
- Virtual Constructors
- Naming Names
- Function Calls
- Speaking Standardese (updated)
- Speaking Standardese: the One Definition Rule
- Declarations and Definitions
- More on Declarations and Definitions
- The Most Vexing Parse
- Finally, At Last
- Sound Bytes (Admittedly Off Topic)
- Local Classes
- Complex Arithmetic
- Floating Point Woes
- String Manipulation
- The Object Model
- The Object Model II
- The Object Model III
- Temporary Objects
- Temporary Objects: Advanced Techniques
- Over-Engineering
- Security Enhancements
- Drop the (automatic) Pilot
- Choosing the Right Container
- Choosing the Right Container II
- Choosing the Right Container, Part III
- Arrays and Pointers
- Low-Level File I/O
- Low-Level File I/O Part II
- static Declarations, Part I
- static Declarations, Part II
- <code>static</code> Initialization Order
- Revisiting the Deprecation of File-Scope Static
- Virtual Memory and Memory Mapping
- Cellular Phone Programming Guidelines
- The Handle/Body Idiom
- Whole Program Optimization, Part I
- Whole Program Optimization, Part II
- Manipulating Directories
- Window Dressing
- <code>friend</code> Declarations
- <code>friend</code> Part II: the Interaction of Friendship and Template Classes
- Forcing Object Allocation on Specific Storage Types
- Lazy Evaluation
- Cache and Carry
- Controlling a Container’s Capacity
- Non-Blocking I/O, Part I
- Non-Blocking I/O, Part II
- Using Unions for Automatic Conversion
- Launching a Child Process
- <tt>switch</tt> Statements
- Introducing the "struct Hack"
- Scoped Enumerators
- Doing Statistics with STL
- Fixing the "Unresolved External" Linkage Error
- Understanding Calling Conventions
- Understanding the Empty Base Optimization
- Implementing RPC with the door Library, Part 1
- Implementing RPC with the door Library, Part 2
- Eliminating Two Common Pointer and <tt>sizeof</tt> Bugs
- Command Line Arguments
- Performance Myths Busting
- Tag Names And Types Part I
- Tag Names And Types Part II
- The Infamous goto
- Trimming Strings
- Can Objects Live Forever? Part I
- Can Objects Live Forever? Part II
- Five Ways to Improve Your Functions
- Member Aggregate Initialization
- Five Futile Coding-Style Debates
- The Good Parasite Idiom: An Exercise in OOD
- The Good Parasite Idiom: An Exercise in OOD, Part II
- The Good Parasite Idiom: An Exercise in OOD, Part III
- Ten Techniques to Reduce the Size of Your Classes, Part I
- Ten Techniques to Reduce the Size of Your Classes, Part II: Inheritance Issues
- Ten Techniques to Reduce the Size of Your Classes, Part III
- Ten Techniques to Reduce the Size of Your Classes IV
- Taking the Address of an Object with an Overloaded Operator <tt>&</tt>
- strcpy() -- How and Why Does It "Just Work"?
- Anonymous Structs
- Five Easy Ways to Reduce The Size of your Executables
- Standard Layout Classes and Trivially Copyable Types, Part I
- Standard Layout Classes and Trivially Copyable Types, Part II
- Five Simple Code Sanity Checks
- Five Things You Need to Know About C++11 Unions
- A Tour of C99
- A Tour of C1X
- C++0X: The New Face of Standard C++
- C++0x Concurrency
- The Reflecting Circle
- We Have Mail
- The Soapbox
- Numeric Types and Arithmetic
- Careers
- Locales and Internationalization
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.
