Operator->
The
operator–>
is generally used when you want to make an object appear to be a pointer. Since
such an object has more “smarts” built into it than exist for a
typical pointer, an object like this is often called a smart pointer.
These are especially useful if you want to “wrap” a class around a
pointer to make that pointer safe, or in the common usage of an
iterator, which is an object that moves through a
collection /container
of other objects and selects them one at a time,
without providing direct access to the implementation of the container.
(You’ll often find containers and iterators in class libraries, such as in
the Standard C++ Library, described in Volume 2 of this book.)
A pointer dereference operator must be a
member function. It has additional, atypical constraints: It must return an
object (or reference to an object) that also has a pointer dereference operator,
or it must return a pointer that can be used to select what the pointer
dereference operator arrow is pointing at. Here’s a simple
example:
//: C12:SmartPointer.cpp
#include <iostream>
#include <vector>
#include "../require.h"
using namespace std;
class Obj {
static int i, j;
public:
void f() const { cout << i++ << endl; }
void g() const { cout << j++ << endl; }
};
// Static member definitions:
int Obj::i = 47;
int Obj::j = 11;
// Container:
class ObjContainer {
vector<Obj*> a;
public:
void add(Obj* obj) { a.push_back(obj); }
friend class SmartPointer;
};
class SmartPointer {
ObjContainer& oc;
int index;
public:
SmartPointer(ObjContainer& objc) : oc(objc) {
index = 0;
}
// Return value indicates end of list:
bool operator++() { // Prefix
if(index >= oc.a.size()) return false;
if(oc.a[++index] == 0) return false;
return true;
}
bool operator++(int) { // Postfix
return operator++(); // Use prefix version
}
Obj* operator->() const {
require(oc.a[index] != 0, "Zero value "
"returned by SmartPointer::operator->()");
return oc.a[index];
}
};
int main() {
const int sz = 10;
Obj o[sz];
ObjContainer oc;
for(int i = 0; i < sz; i++)
oc.add(&o[i]); // Fill it up
SmartPointer sp(oc); // Create an iterator
do {
sp->f(); // Pointer dereference operator call
sp->g();
} while(sp++);
} ///:~
The class Obj defines the objects
that are manipulated in this program. The functions f( ) and
g( ) simply print out interesting values using static data
members. Pointers to these objects are stored inside containers of type
ObjContainer using its add( ) function. ObjContainer
looks like an array of pointers, but you’ll notice there’s no way to
get the pointers back out again. However, SmartPointer is declared as a
friend class, so it has permission to look inside the container. The
SmartPointer class looks very much like an intelligent pointer –
you can move it forward using operator++ (you can also define an
operator– –), it won’t go past the end of the container
it’s pointing to, and it produces (via the pointer dereference operator)
the value it’s pointing to. Notice that the SmartPointer is a
custom fit for the container it’s created for; unlike an ordinary pointer,
there isn’t a “general purpose” smart pointer. You will learn
more about the smart pointers called “iterators” in the last chapter
of this book and in Volume 2 (downloadable from
www.BruceEckel.com).
In main( ), once the
container oc is filled with Obj objects, a SmartPointer sp
is created. The smart pointer calls happen in the expressions:
sp->f(); // Smart pointer calls
sp->g();
Here, even though sp doesn’t
actually have f( ) and g( ) member functions, the
pointer dereference operator automatically calls those functions for the
Obj* that is returned by SmartPointer::operator–>. The
compiler performs all the checking to make sure the function call works
properly.
Although the underlying mechanics of the
pointer dereference operator are more complex than the other operators, the goal
is exactly the same: to provide a more convenient syntax for the users of your
classes.