Aside from making a grammatically awkward sentence, what is the "this" pointer, and why is it important? To help us answer these questions, we expand on our initial introduction to the this
pointer and its example statement. We'll explore how C++ initializes the value saved in this and how it binds an object to a member function by converting a stack structure and its supporting functions into a class with corresponding member functions.
const int SIZE = 100; struct stack { char st[SIZE]; int sp; }; stack make_stack(); void init_stack(stack* s); void push(stack* s, char data); char pop(stack* s); int size(stack* s); char peek(stack* s); |
void push(stack* s, char data) { if (s->sp < SIZE) s->st[s->sp++] = data; else cerr << "ERROR: stack is full" << endl; } char pop(stack* s) { if (s->sp > 0) return s->st[s->--sp]; else cerr << "ERROR: stack is empty" << endl; } |
(a) | (b) |
I included make_stack to maintain some consistency between the original stack example and previous versions of the Time example, but it's not needed. Except for make_stack, each function requires a pointer-to-a-stack argument. Three functions, init_stack, push, and pop, modify the stack, so we must pass the stack argument using an INOUT technique - a pointer or reference. The example uses a pointer to set the stage for the this
pointer.
this
PointerA natural question to ask ourselves at this time is, "If we always pass a stack pointer to every stack function, can we automate the process to make it easier?" And the answer is that we can! Let's begin by rewriting the stack structure as a class:
Converting the stack from a structure to a class changes the appearance of the functions but not their behavior. When called, the functions must still "know" on which stack they are operating, which is equivalent to saying that each function must be bound to an object when it runs. Furthermore, this binding occurs in the same way as in the structure version: the program passes the stack's address to the function as an argument. The programmer must explicitly calculate the stack's address in the structure version and pass the address as an explicit argument. The C++ compiler does both steps automatically for member functions.
Whenever the program calls a member function, the calling object's address is automatically passed as an "invisible" or implicit pointer argument. Invisible or not, the program saves every value it passes to a function in a local parameter variable. In the struct version of the stack, that argument and how the function uses it are visible in the function definitions, as illustrated in Figure 1. In the class version of the stack, the compiler automatically passes the address of the bound object and generates the "receiving" variable or parameter, naming it this
. The following figure contrasts the code that a programmer writes with the code that the compiler automatically generates.
Programmer Writes | Compiler Generates |
---|---|
s.push('a'); |
s.push(&s, 'a'); |
void stack::push(char data) { if (sp < SIZE) st[sp++] = data; else cerr << "ERROR: stack is full" << endl; } |
void stack::push(stack* this, char data) { if (this->sp < SIZE) this->st[this->sp++] = data; else cerr << "ERROR: stack is full" << endl; } |
stack s;
.this
pointer appears just as clearly as the explicit arguments.this->st[this->sp++] = data;
and st[sp++] = data;
are valid and produce the same results.this
The this
pointer is an automatic local variable that the compiler generates for every non-static member function, saving the address of the object that calls the function. The object-to-function binding through the this
pointer begins when any non-static member function is called and lasts until the function ends and returns. For example, given the function call o.f();, while function f is running, this == &o.
The this
pointer is a predefined parameter whose memory the program automatically allocates whenever it calls a non-static member function and deallocates when the function ends, making the binding between an object and a member function temporary. The program establishes the binding when it calls the function and dissolves it when it ends. Any stack object can play the role of the implicit or "this" object for any of the member functions:
stack r; stack s; stack t; |
|
(a) | |
s.push('a'); |
|
(b) | |
t.push('a'); |
|
(c) |
this
binds objects to member functions. this
is an implicitly defined automatic parameter defined in all non-static member functions. Like all automatic local variables, the program allocates and deallocates its memory when calling and returning from the function.
this
stores the address of s while the push operation is runningthis
stores the address of t while the push operation is running