14.7. Member Access Operators
The dereference (*
) and arrow (->
) operators are often used in classes that represent iterators and in smart pointer classes (§ 12.1, p. 450). We can logically add these operators to our StrBlobPtr
class as well:
class StrBlobPtr {
public:
std::string& operator*() const
{
auto p = check(curr, "dereference past end");
return (*p)[curr]; // (*p) is the vector to which this object points
}
std::string* operator->() const
{
// delegate the real work to the dereference operator
return & this->operator*();
}
// other members as before
};
The dereference operator checks that curr
is still in range and, if so, returns a reference to the element denoted by curr
. The arrow operator avoids doing any work of its own by calling the dereference operator and returning the address of the element returned by that operator.
INFO
Operator arrow must be a member. The dereference operator is not required to be a member but usually should be a member as well.
It is worth noting that we’ve defined these operators as const
members. Unlike the increment and decrment operators, fetching an element doesn’t change the state of a StrBlobPtr
. Also note that these operators return a reference or pointer to nonconst string
. They do so because we know that a StrBlobPtr
can only be bound to a nonconst StrBlob
(§ 12.1.6, p. 474).
We can use these operators the same way that we’ve used the corresponding operations on pointers or vector
iterators:
StrBlob a1 = {"hi", "bye", "now"};
StrBlobPtr p(a1); // p points to the vector inside a1
*p = "okay"; // assigns to the first element in a1
cout << p->size() << endl; // prints 4, the size of the first element in a1
cout << (*p).size() << endl; // equivalent to p->size()
Constraints on the Return from Operator Arrow
As with most of the other operators (although it would be a bad idea to do so), we can define operator*
to do whatever processing we like. That is, we can define operator*
to return a fixed value, say, 42
, or print the contents of the object to which it is applied, or whatever. The same is not true for overloaded arrow. The arrow operator never loses its fundamental meaning of member access. When we overload arrow, we change the object from which arrow fetches the specified member. We cannot change the fact that arrow fetches a member.
When we write point->mem
, point
must be a pointer to a class object or it must be an object of a class with an overloaded operator->
. Depending on the type of point
, writing point->mem
is equivalent to
(*point).mem; // point is a built-in pointer type
point.operator()->mem; // point is an object of class type
Otherwise the code is in error. That is, point->mem
executes as follows:
- If
point
is a pointer, then the built-in arrow operator is applied, which means this expression is a synonym for(*point).mem
. The pointer is dereferenced and the indicated member is fetched from the resulting object. If the type pointed to bypoint
does not have a member namedmem
, then the code is in error. - If
point
is an object of a class that definesoperator->
, then the result ofpoint.operator->()
is used to fetchmem
. If that result is a pointer, then step 1 is executed on that pointer. If the result is an object that itself has an overloadedoperator->()
, then this step is repeated on that object. This process continues until either a pointer to an object with the indicated member is returned or some other value is returned, in which case the code is in error.
INFO
The overloaded arrow operator must return either a pointer to a class type or an object of a class type that defines its own operator arrow.
INFO
Exercises Section 14.7
Exercise 14.30: Add dereference and arrow operators to your StrBlobPtr
class and to the ConstStrBlobPtr
class that you defined in exercise 12.22 from § 12.1.6 (p. 476). Note that the operators in constStrBlobPtr
must return const
references because the data
member in constStrBlobPtr
points to a const vector
.
Exercise 14.31: Our StrBlobPtr
class does not define the copy constructor, assignment operator, or a destructor. Why is that okay?
Exercise 14.32: Define a class that holds a pointer to a StrBlobPtr
. Define the overloaded arrow operator for that class.