Operator->*
The operator–>*
is a binary operator that
behaves like all the other binary operators. It is provided for those situations
when you want to mimic the behavior provided by the built-in
pointer-to-member syntax, described in the
previous chapter.
Just like operator->, the
pointer-to-member dereference operator is generally used with some kind of
object that represents a “smart pointer,” although the example shown
here will be simpler so it’s understandable. The trick when defining
operator->* is that it must return an object for which the
operator( ) can be called with the arguments for the member function
you’re calling.
The function call
operator( )
must be a member function, and it is unique in that it allows any number of
arguments. It makes your object look like it’s actually a function.
Although you could define several overloaded operator( ) functions
with different arguments, it’s often used for types that only have a
single operation, or at least an especially prominent one. You’ll see in
Volume 2 that the Standard C++ Library uses the function call operator in order
to create “function objects.”
To create an operator->* you
must first create a class with an operator( ) that is the type of
object that operator->* will return. This class must somehow capture
the necessary information so that when the operator( ) is called
(which happens automatically), the pointer-to-member will be dereferenced for
the object. In the following example, the FunctionObject constructor
captures and stores both the pointer to the object and the pointer to the member
function, and then the operator( ) uses those to make the actual
pointer-to-member call:
//: C12:PointerToMemberOperator.cpp
#include <iostream>
using namespace std;
class Dog {
public:
int run(int i) const {
cout << "run\n";
return i;
}
int eat(int i) const {
cout << "eat\n";
return i;
}
int sleep(int i) const {
cout << "ZZZ\n";
return i;
}
typedef int (Dog::*PMF)(int) const;
// operator->* must return an object
// that has an operator():
class FunctionObject {
Dog* ptr;
PMF pmem;
public:
// Save the object pointer and member pointer
FunctionObject(Dog* wp, PMF pmf)
: ptr(wp), pmem(pmf) {
cout << "FunctionObject constructor\n";
}
// Make the call using the object pointer
// and member pointer
int operator()(int i) const {
cout << "FunctionObject::operator()\n";
return (ptr->*pmem)(i); // Make the call
}
};
FunctionObject operator->*(PMF pmf) {
cout << "operator->*" << endl;
return FunctionObject(this, pmf);
}
};
int main() {
Dog w;
Dog::PMF pmf = &Dog::run;
cout << (w->*pmf)(1) << endl;
pmf = &Dog::sleep;
cout << (w->*pmf)(2) << endl;
pmf = &Dog::eat;
cout << (w->*pmf)(3) << endl;
} ///:~
Dog has three member functions,
all of which take an int argument and return an int. PMF is
a typedef to simplify defining a pointer-to-member to Dog’s
member functions.
A FunctionObject is created and
returned by operator->*. Notice that operator->* knows both
the object that the pointer-to-member is being called for (this) and the
pointer-to-member, and it passes those to the FunctionObject constructor
that stores the values. When operator->* is called, the compiler
immediately turns around and calls operator( ) for the return value
of operator->*, passing in the arguments that were given to
operator->*. The FunctionObject::operator( ) takes the
arguments and then dereferences the “real” pointer-to-member using
its stored object pointer and pointer-to-member.
Notice that what you are doing here, just
as with operator->, is inserting yourself in the middle of the call to
operator->*. This allows you to perform some extra operations if you
need to.
The operator->* mechanism
implemented here only works for member functions that take an int
argument and return an int. This is limiting, but if you try to
create overloaded mechanisms for each different possibility, it seems like a
prohibitive task. Fortunately, C++’s template mechanism (described
in the last chapter of this book, and in Volume 2) is designed to handle just
such a
problem.