An example
The value of a pointer is that you can
change what it points to at runtime, which provides an important flexibility in
your programming because through a pointer you can select or change
behavior at runtime. A pointer-to-member is no different; it allows you
to choose a member at runtime. Typically, your classes will only have member
functions publicly visible (data members are usually considered part of the
underlying implementation), so the following example selects member functions at
runtime.
//: C11:PointerToMemberFunction.cpp
#include <iostream>
using namespace std;
class Widget {
public:
void f(int) const { cout << "Widget::f()\n"; }
void g(int) const { cout << "Widget::g()\n"; }
void h(int) const { cout << "Widget::h()\n"; }
void i(int) const { cout << "Widget::i()\n"; }
};
int main() {
Widget w;
Widget* wp = &w;
void (Widget::*pmem)(int) const = &Widget::h;
(w.*pmem)(1);
(wp->*pmem)(2);
} ///:~
Of course, it isn’t particularly
reasonable to expect the casual user to create such complicated expressions. If
the user must directly manipulate a pointer-to-member, then a typedef is
in order. To really clean things up, you can use the pointer-to-member as part
of the internal implementation mechanism. Here’s the preceding example
using a pointer-to-member inside the class. All the user needs to do is
pass a number in to select a
function.[48]
//: C11:PointerToMemberFunction2.cpp
#include <iostream>
using namespace std;
class Widget {
void f(int) const { cout << "Widget::f()\n"; }
void g(int) const { cout << "Widget::g()\n"; }
void h(int) const { cout << "Widget::h()\n"; }
void i(int) const { cout << "Widget::i()\n"; }
enum { cnt = 4 };
void (Widget::*fptr[cnt])(int) const;
public:
Widget() {
fptr[0] = &Widget::f; // Full spec required
fptr[1] = &Widget::g;
fptr[2] = &Widget::h;
fptr[3] = &Widget::i;
}
void select(int i, int j) {
if(i < 0 || i >= cnt) return;
(this->*fptr[i])(j);
}
int count() { return cnt; }
};
int main() {
Widget w;
for(int i = 0; i < w.count(); i++)
w.select(i, 47);
} ///:~
In the class interface and in
main( ), you can see that the entire implementation, including the
functions, has been hidden away. The code must even ask for the
count( ) of functions. This way, the class implementer can change
the quantity of functions in the underlying implementation without affecting the
code where the class is used.
The initialization of the
pointers-to-members in the constructor may seem overspecified. Shouldn’t
you be able to say
fptr[1] = &g;
because the name g occurs in the
member function, which is automatically in the scope of the class? The problem
is this doesn’t conform to the pointer-to-member syntax, which is required
so everyone, especially the compiler, can figure out what’s going on.
Similarly, when the pointer-to-member is dereferenced, it seems
like
(this->*fptr[i])(j);
is also over-specified; this looks
redundant. Again, the syntax requires that a pointer-to-member always be bound
to an object when it is
dereferenced.