| [ Team LiB ] |
|
Gotcha #24: Operator -> SubtletiesThe predefined -> operator is binary: the left operand is a pointer, and the right operand is the name of a class member. Overloaded operator -> is a unary member function!
class Ptr {
public:
Ptr( T *init );
T *operator ->();
// . . .
private:
T *tp_;
};
An invocation of overloaded -> must return something that may then be used with an -> operator to access a member:
Ptr p( new T ); p->f(); // p.operator ->()->f()! One way to look at it is that the -> token is not "consumed" by an overloaded operator -> but is instead retained for eventual use as the predefined -> operator. Typically, some additional semantics are attached to the overloaded -> to create a "smart pointer" type:
T *Ptr::operator ->() {
if( today() == TUESDAY )
abort();
else
return tp_;
}
We mentioned that an overloaded operator -> must return "something" that may be then used to access a member. That something doesn't have to be a predefined pointer. It could be a class object that itself overloads operator ->:
class AugPtr {
public:
AugPtr( T *init ) : p_( init ) {}
Ptr &operator ->();
// . . .
private:
Ptr p_;
};
Ptr &AugPtr::operator ->() {
if( today() == FRIDAY )
cout << '\a' << flush;
return p_;
}
This allows the cascading of smart pointer responsibilities:
AugPtr ap( new T ); ap->f(); // ap.operator ->().operator ->()->f()! Note that the sequence of operator -> activations is always determined statically by the type of the object that contains the operator -> definition, and the sequence of operator -> member function calls always terminates in a call that returns a predefined pointer to class. For example, applying the -> operator to an AugPtr will always result in the sequence of calls AugPtr::operator -> followed by Ptr::operator ->, followed by a predefined -> operator applied to a T * pointer. (See Gotcha #83 for a more realistic example of the use of operator ->.) |
| [ Team LiB ] |
|