Aside from making a grammatically awkward sentence, what is the "this" pointer, and why is it important? To help answer these questions, we expand on our initial introduction to the this pointer and the accompanying example. We'll explore how C++ programs initialize the this pointer to bind an object to a member function. To demonstrate one way that objects simplify programming, the section converts the Chapter 7 structure-based stack and its associated functions to a class with 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) |
The following figures convert the structure-based stack to a class with member functions. The conversion merges make_stack and init_stack into a single default constructor. Three functions, the constructor, push, and pop modify the stack, requiring an INOUT passing mechanism - a pointer or a reference. Follow the parameter, stack* s and its corresponding argument throughout the conversion process.
this PointerA natural question to ask is, "If we always pass a stack pointer to every stack function, can the compiler automate the process to make it easier?" Fortunately, it can! The following figure begins by converting the stack structure to a class:
class stack
{
private:
static const int SIZE = 100;
char st[SIZE];
int sp;
public:
stack() : sp(0) {}
void push(char data);
char pop();
int size();
char peek();
};
static member. make_stack and init_stack are replaced with a single default constructor. The object-oriented version makes all the stackfunctions members of the class. Significantly, the member functions no longer need the explicit parameter, stack* s, and the class removes them.
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 means 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 it 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 a "receiving" variable or parameter named 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
throw "Stack Overflow";
} |
void stack::push(stack* this, char data) { if (this->sp < SIZE) this->st[this->sp++] = data; else throw "Stack Overflow"; } |
this-> in the source code, but it is optional: this->st[this->sp++] = data; and st[sp++] = data; are equivalent 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 the program calls any non-static member function and lasts until the function ends and returns. Let o represent an arbitrary object with the member function f; while f is running, within its body or scope, this == &o.
#include<iostream>
#include "stack.h"
using namespace std;
void stack::push(char data)
{
if (sp < SIZE)
st[sp++] = data;
else
throw "Stack Overflow";
}
char stack::pop()
{
if (sp > 0)
return st[--sp];
else
throw "Stack Underflow";
}
int stack::size()
{
return sp;
}
char stack::peek()
{
return st[sp - 1];
}
The compiler automatically defines a transparent pointer parameter named this in every non-static member function. Furthermore, the compiler adds the address of the calling object as an implicit argument to every member function call. Therefore, the program establishes the binding between an object and a member function when it calls the function and dissolves it when it ends, making the binding between an object and a member function temporary.
stack r; stack s; stack t; |
|
| (a) | |
s.push('a');
|
|
| (b) | |
t.push('a');
|
|
| (c) | |
this binds objects to member functions. Although not visible in a member function definition, the compiler automatically adds this as a parameter. Like all automatic local variables, the program allocates and deallocates its memory for it when calling and returning from the function. In this example, the program can pass the address of any stack object to the push function, binding the object and the function with the this parameter.
this stores the address of s while the push operation is runningthis stores the address of t while the push operation is running| View | Download | Comments |
|---|---|---|
| stack.h | access.h | |
| stack.cpp | stack.cpp |