Professional C__ - Marc Gregoire [323]
Implementing operator*
When you dereference a pointer, you expect to be able to access the memory to which the pointer points. If that memory contains a simple type such as an int, you should be able to change its value directly. If the memory contains a more complicated type, such as an object, you should be able to access its data members or methods with the . operator.
To provide these semantics, you should return a reference to a variable or object from operator*. In the Pointer class, the declaration and definition look like this:
template class Pointer { public: Pointer(T* inPtr); virtual ~Pointer(); T& operator*(); const T& operator*() const; // Omitted for brevity }; template T& Pointer { return *mPtr; } Code snippet from DereferenceOps\Pointer.h As you can see, operator* returns a reference to the object or variable to which the underlying dumb pointer points. As with overloading the subscripting operators, it’s useful to provide both const and non-const versions of the method, which return a const reference and a non-const reference, respectively. The const version is implemented identically to the non-const version, so its implementation is not shown here. Implementing operator-> The arrow operator is a bit trickier. The result of applying the arrow operator should be a member or method of an object. However, in order to implement it like that, you would have to be able to implement the equivalent of operator* followed by operator.; C++ doesn’t allow you to overload operator. for good reason: It’s impossible to write a single prototype that allows you to capture any possible member or method selection. Therefore, C++ treats operator-> as a special case. Consider this line: smartCell->set(5); C++ translates this to: (smartCell.operator->())->set(5); As you can see, C++ applies another operator-> to whatever you return from your overloaded operator->. Therefore, you must return a pointer to an object, like this: template class Pointer { public: // Omitted for brevity T* operator->(); const T* operator->() const; // Omitted for brevity }; template T* Pointer { return mPtr; } Code snippet from DereferenceOps\Pointer.h Again, you should write both const and non-const versions of the operator. The implementation of the const version is identical to the non-const, so it is not shown here. You may find it confusing that operator* and operator-> are asymmetric, but, once you see them a few times, you’ll get used to it. What in the World Is operator ->* ? It’s perfectly legitimate in C++ to take the addresses of class members and methods in order to obtain pointers to them. However, you can’t access a non-static member or call a non-static method without an object. The whole point of class members and methods is that they exist on a per-object basis. Thus, when you want to call the method or access the member via the pointer, you must dereference the pointer in the context of an object. The following example demonstrates this. Chapter 21 discusses the syntactical details in the section called “Pointers to Methods and Members.” You can ignore these details for this example; the only important parts for now are the .* and the ->* operators: SpreadsheetCell myCell; double (SpreadsheetCell::*methodPtr) () const = &SpreadsheetCell::getValue; cout << (myCell.*methodPtr)() << endl; Note the use of the .* operator to dereference the method pointer and call the method. There is also an equivalent operator->* for calling methods via pointers when you have a pointer to an object instead of the object itself. The operator looks like this: SpreadsheetCell* myCell = new SpreadsheetCell(); double (SpreadsheetCell::*methodPtr) () const = &SpreadsheetCell::getValue; cout << (myCell->*methodPtr)()