Академический Документы
Профессиональный Документы
Культура Документы
Abstract
Pointers to Member Functions are one of C++'s more rarely used features, and are often
not well understood even by experienced developers. This is understandable, as their
syntax is necessarily rather clumsy and obscure.
While they do not have wide applicability, sometimes member function pointers are
useful to solve certain problems, and when they do apply they are often the perfect
choice, both for improved performance and to make the code sensible. They work very
well to cache the result of a frequently made decision, and to implement a different sort
of polymorphism.
I discuss what member function pointers are, how to declare and use them, and give some
examples of problems that they solve very well.
Contents
Abstract
Introduction
Member Function Pointers Are Not Just Simple Addresses
Caching the Outcome of a Decision
The Performance of Member Function Pointers
Details About Using Member Function Pointers
A Different Sort of Polymorphism
Introduction
I don't have any hard numbers on how frequently member function pointers are used.
While I do see others mention them sometimes in Usenet and mailing list posts, I have
yet to find someone else use one in code I have worked with, so my impression is that
they are not commonly applied.
Member function pointers are important because they provide an efficient way to cache
the outcome of a decision over which member function to call. They can save time, and
in some cases, provide a design alternative that avoids the need to implement such
decision caching through memory allocation. I will return to this further on.
Member function pointers allow one to call one of several of an object's member
functions indirectly. Each of the functions whose "address" is stored must share the same
signature.
I put "address" in quotes because the information stored in a member function pointer is
not simply the memory address of the start of the member function's code; conceptually it
is an offset into the list of functions declared by the class, and in the case of virtual
functions will include a real offset into the vtbl, or table of virtual function pointers.
Member function pointers cannot be dereferenced (have their function called) directly by
themselves. They must be called on behalf of some object, that then provides the "this"
pointer for use by the member functions.
To illustrate how to declare and call a member function pointer, I will start by giving an
example of declaring and dereferencing an ordinary pointer to a non-member function.
You declare a function pointer by giving the prototype of a function it can point to, with
the name of the function replaced by (*pointerName). Regular function pointers share
the same syntax between C and C++:
void Foo( int anInt, double aDouble );
void Bar()
{
void (*funcPtr)( int, double ) = &Foo;
}
(*funcPtr)( 1, 2.0 );
For regular function pointers, it is optional to use the address-of operator & when taking
the address of a function, but it is required for taking the address of member functions. g+
+ will compile source that leaves it out, but emits a warning.
To declare a pointer to member function, you give the prototype of a function it can point
to, as before, but the name of this function is replaced by a construction that scopes the
pointer - you give it the name of the class whose member functions it can point to, as
(ClassName::*pointerName). Note that a given member function pointer can only
point to functions that are members of the class it was declared with. It cannot be applied
to an object of a different class even if it has member functions with the same signature.
Declaring a member function pointer is clumsy at best and is hard to get right until you
have used them for a while. Rather than declaring them using the full prototype each
time, it is helpful to use a typedef as I show in the example below.
pointer returned by a function that supplies them may have a different size than the
recipient function expects, causing bogus data to be overwritten on the stack.
There is an item in VC++'s settings labeled "representation" that has a choice between
"best case always" and "most general always". If you work with member function
pointers in Visual C++, check the documentation for what these settings do and select the
right one; if in doubt, select "most general always".
TimesOne() const;
TimesTwo() const;
TimesThree() const;
mVal;
};
typedef long (Test::*Multiplier)() const;
int main( int
{
using
using
using
if ( argc != 3 ){
cerr << "Usage: PtrTest value factor"
return 1;
}
Multiplier funcPtr;
<< endl;
<< endl;
<< (myTest.*funcPtr)()
<<endl;
return 0;
}
long Test::TimesOne() const
{
return mVal;
}
long Test::TimesTwo() const
{
return 2 * mVal;
}
long Test::TimesThree() const
{
return 3 * mVal;
}
Now I present an example that does not perform as well as it could because performs a
switch decision many times inside a loop, always reaching the same decision. It is a
good candidate to refactor by using a pointer to member function. Again it is a silly
example but I wanted to be very clear:
#include <exception>
class Test
{
public:
In most cases where an identical decision is made inside a loop, it is better to refactor the
code so that the decision is outside the loop, and the loop is repeated in each branch of
the loop (or packaged inside a subroutine):
void Foo( long value )
{
for ( long i = 0; i < 1000000; ++i ){
switch( value ){
// BAD CODE: always
reaches the same decision
case 1:
//...
break;
case 2:
//...
break;
case 3:
//...
break;
If you want to avoid repeating the loop implementations and each branch of the decision
has similar code, you can place them inside subroutines.
Member function pointers are the best solution when it is not practical to refactor this
way. One reason might be that the loop and the decision are in code that belongs to
different classes, and you do not want to expose the implementation of the class that
makes the decision. Here is the MultiplyIt code above, refactored to use a pointer to
member function:
#include <exception>
class Test
{
public:
default:
throw std::exception();
}
long Test::MultiplyIt( long inToMultiply ) const
{
// Using cached decision result
return (this->*mMultFuncPtr)( inToMultiply );
}
void MultiplyThem( long inFactor )
{
Test myTest( 2 );
long product;
for ( long i = 0; i < 1000000; ++i )
product = myTest.MultiplyIt( i );
}
assembly code provided by CodeWarrior's library. This is pretty fast code, and will run
very fast in a tight loop if it stays in the CPU's L1 cache, but it is not as fast as a simple
compare and conditional branch.
If the decision your code is making repeatedly is very quick to run, it may not be to your
advantage to use a member function pointer. A simple if statement that compares two
numeric values, or checks the value of a bool, or possibly a switch statement whose
alternatives are all contained in a small range (so it is easy for the compiler to build a
jump table) may be quicker than dereferencing a member function pointer.
However, if the decision is complicated or lengthy to arrive at, like string comparison or
searching some data structure, then using a pointer to member function may be a big win.
};
void Test()
{
Different::FuncPtr ptr = &Different::InlineMember;
ptr = &Different::VirtualMember;
}
ptr = &Different::OrdinaryMember;
(You may be surprised to see me creating a pointer to an inline function, but this is
perfectly normal. If you do this, the compiler will place a normal subroutine version of
the inline's implementation in an object file and give you the address of that, so the
function pointer does not really point to an inline function at all.)
However, although a static member function may appear to have the same calling
convention, it really does not because it is not passed the this pointer - this is passed to
your member functions just like any other parameter, but it is not given explicitly in the
member function's prototype. You cannot use pointers to member functions to store the
address of a static function (use an ordinary, non-member function pointer for that):
void Fails()
{
Different::FuncPtr ptr = &Different::StaticMember;
}
mike% c++ different.cpp
different.cpp: In function `void Fails()':
different.cpp:24: initialization to `void (Different::*)()' from `void
(*)()'
Pointers to virtual member functions work just like calling a virtual member function
directly - the type whose member function gets called is the dynamic type of the object it
is called on behalf of, not the static type of the member function pointer:
#include <iostream>
class Base
{
public:
virtual void WhoAmI() const;
};
return 0;