C++ Function Pointer Tutorial (LARS HAENDEL)
C++ Function Pointer Tutorial (LARS HAENDEL)
Contents
1 Introduction to Function Pointers 2
1.1 What is a Function Pointer ? . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2
1.2 Introductory Example or How to Replace a Switch-Statement . . . . . . . . . . . . . . . . . . . . 2
1
1 Introduction to Function Pointers
Function Pointers provide some extremely interesting, efficient and elegant programming techniques. You can
use them to replace switch/if-statements, to realize your own late-binding or to implement callbacks. Unfor-
tunately – probably due to their complicated syntax – they are treated quite stepmotherly in most computer
books and documentations. If at all, they are addressed quite briefly and superficially. They are less error prone
than normal pointers cause you will never allocate or de-allocate memory with them. All you’ve got to do is
to understand what they are and to learn their syntax. But keep in mind: Always ask yourself if you really
need a function pointer. It’s nice to realize one’s own late-binding but to use the existing structures of C++
may make your code more readable and clear. One aspect in the case of late-binding is runtime: If you call a
virtual function, your program has got to determine which one has got to be called. It does this using a V-Table
containing all the possible functions. This costs some time each call and maybe you can save some time using
function pointers instead of virtual functions. Maybe not ... 1
//------------------------------------------------------------------------------------
// 1.2 Introductory Example or How to Replace a Switch-Statement
// Task: Perform one of the four basic arithmetic operations specified by the
// characters ’+’, ’-’, ’*’ or ’/’.
2
cout << "Switch: 2+5=" << result << endl; // display result
}
Important note: A function pointer always points to a function with a specific signature! Thus all functions,
you want to use with the same function pointer, must have the same parameters and return-type!
3
// 2.2 define the calling convention
void __cdecl DoIt(float a, char b, char c); // Borland and Microsoft
void DoIt(float a, char b, char c) __attribute__((cdecl)); // GNU GCC
// C
int DoIt (float a, char b, char c){ printf("DoIt\n"); return a+b+c; }
int DoMore(float a, char b, char c)const{ printf("DoMore\n"); return a-b+c; }
// C++
class TMyClass
{
public:
int DoIt(float a, char b, char c){ cout << "TMyClass::DoIt"<< endl; return a+b+c;};
int DoMore(float a, char b, char c) const
{ cout << "TMyClass::DoMore" << endl; return a-b+c; };
/* more of TMyClass */
};
// C
if(pt2Function >0){ // check if initialized
if(pt2Function == &DoIt)
printf("Pointer points to DoIt\n"); }
else
printf("Pointer not initialized!!\n");
// C++
if(pt2ConstMember == &TMyClass::DoMore)
cout << "Pointer points to TMyClass::DoMore" << endl;
4
2.5 Calling a Function using a Function Pointer
In C you call a function using a function pointer by explicitly dereferencing it using the * operator. Alternatively
you may also just use the function pointer’s instead of the funtion’s name. In C++ the two operators .* resp.
->* are used together with an instance of a class in order to call one of their (non-static) member functions. If
the call takes place within another member function you may use the this-pointer.
TMyClass instance1;
int result3 = (instance1.*pt2Member)(12, ’a’, ’b’); // C++
int result4 = (*this.*pt2Member)(12, ’a’, ’b’); // C++ if this-pointer can be used
//------------------------------------------------------------------------------------
// 2.6 How to Pass a Function Pointer
// <pt2Func> is a pointer to a function which returns an int and takes a float and two char
void PassPtr(int (*pt2Func)(float, char, char))
{
int result = (*pt2Func)(12, ’a’, ’b’); // call using function pointer
cout << result << endl;
}
// execute example code - ’DoIt’ is a suitable function like defined above in 2.1-4
void Pass_A_Function_Pointer()
{
cout << endl << "Executing ’Pass_A_Function_Pointer’" << endl;
PassPtr(&DoIt);
}
//------------------------------------------------------------------------------------
// 2.7 How to Return a Function Pointer
// ’Plus’ and ’Minus’ are defined above. They return a float and take two float
5
// Solution using a typedef: Define a pointer to a function which is taking
// two floats and returns a float
typedef float(*pt2Func)(float, float);
// C ---------------------------------------------------------------------------------
// define arrays and ini each element to NULL, <funcArr1> and <funcArr2> are arrays
// with 10 pointers to functions which return an int and take a float and two char
6
// assign the function’s address - ’DoIt’ and ’DoMore’ are suitable functions
// like defined above in 2.1-4
funcArr1[0] = funcArr2[1] = &DoIt;
funcArr1[1] = funcArr2[0] = &DoMore;
/* more assignments */
// C++ -------------------------------------------------------------------------------
// define arrays and ini each element to NULL, <funcArr1> and <funcArr2> are
// arrays with 10 pointers to member functions which return an int and take
// a float and two char
// assign the function’s address - ’DoIt’ and ’DoMore’ are suitable member
// functions of class TMyClass like defined above in 2.1-4
funcArr1[0] = funcArr2[1] = &TMyClass::DoIt;
funcArr1[1] = funcArr2[0] = &TMyClass::DoMore;
/* more assignments */
7
the sort-function sort the items of the field without any information about the type of an item? The answer is
simple: The function receives the pointer to a comparison-function which takes void-pointers to two field-items,
evaluates their ranking and returns the result coded as an int. So every time the sort algorithm needs a decision
about the ranking of two items, it just calls the comparison-function via the function pointer.
field points to the first element of the field which is to be sorted, nElements is the number of items in the
field, sizeOfAnElement the size of one item in bytes and cmpFunc is the pointer to the comparison function.
This comparison function takes two void-pointers and returns an int. The syntax, how you use a function
pointer as a parameter in a function-definition looks a little bit strange. Just review, how to define a function
pointer and you’ll see, it’s exactly the same. A callback is done just like a normal function call would be
done: You just use the name of the function pointer instead of a function name. This is shown below. Note:
All calling arguments other than the function pointer were omitted to focus on the relevant things.
if(*a > *b) return 1; // first item is bigger than the second one -> return 1
else
if(*a == *b) return 0; // equality -> return 0
else return -1; // second item is bigger than the first one -> return -1
}
8
field[c]=random(99);
Example A: Pointer to a class instance passed as an additional argument The function DoItA does
something with objects of the class TClassA which implies a callback. Therefore a pointer to an object of class
TClassA and a pointer to the static wrapper function TClassA::Wrapper To Call Display are passed to DoItA.
This wrapper is the callback-function. You can write arbitrary other classes like TClassA and use them with
DoItA as long as these other classes provide the necessary functions. Note: This solution may be useful if you
design the callback interface yourself. It is much better than the second solution which uses a global variable.
//-----------------------------------------------------------------------------------------
// 3.5 Example A: Callback to member function using an additional argument
// Task: The function ’DoItA’ makes something which implies a callback to
// the member function ’Display’. Therefore the wrapper function
// ’Wrapper_To_Call_Display is used.
class TClassA
{
public:
/* more of TClassA */
};
5 If you use a global variable it is very important that you make sure that it will always point to the correct object!
9
// static wrapper function to be able to callback the member function Display()
void TClassA::Wrapper_To_Call_Display(void* pt2Object, char* string)
{
// explicitly cast to a pointer to TClassA
TClassA* mySelf = (TClassA*) pt2Object;
// call member
mySelf->Display(string);
}
pt2Function(pt2Object, "hi, i’m calling back using a argument ;-)"); // make callback
}
Example B: Pointer to a class instance is stored in a global variable The function DoItB does
something with objects of the class TClassB which implies a callback. A pointer to the static wrapper function
TClassB::Wrapper To Call Display is passed to DoItB. This wrapper is the callback-function. The wrapper
uses the global variable void* pt2Object and explicitly casts it to an instance of TClassB. It is very important,
that you always initialize the global variable to point to the correct class instance. You can write arbitrary other
classes like TClassB and use them with DoItB as long as these other classes provide the necessary functions.
Note: This solution may be useful if you have an existing callback interface which cannot be changed. It is
not a good solution because the use of a global variable is very dangerous and could cause serious errors.
//-----------------------------------------------------------------------------------------
// 3.5 Example B: Callback to member function using a global variable
// Task: The function ’DoItB’ makes something which implies a callback to
// the member function ’Display’. Therefore the wrapper function
// ’Wrapper_To_Call_Display is used.
class TClassB
{
public:
void Display(const char* text) { cout << text << endl; };
static void Wrapper_To_Call_Display(char* text);
/* more of TClassB */
};
10
// static wrapper function to be able to callback the member function Display()
void TClassB::Wrapper_To_Call_Display(char* string)
{
// explicitly cast global variable <pt2Object> to a pointer to TClassB
// warning: <pt2Object> MUST point to an appropriate object!
TClassB* mySelf = (TClassB*) pt2Object;
// call member
mySelf->Display(string);
}
6 If you prefer you can also use a function called Execute or something like that.
11
//-----------------------------------------------------------------------------------------
// 4.2 How to Implement Functors
public:
// dummy class A
class TClassA{
public:
TClassA(){};
void Display(const char* text) { cout << text << endl; };
/* more of TClassA */
};
12
// dummy class B
class TClassB{
public:
TClassB(){};
void Display(const char* text) { cout << text << endl; };
/* more of TClassB */
};
// main program
int main(int argc, char* argv[])
{
// 1. instantiate objects of TClassA and TClassB
TClassA objA;
TClassB objB;
// 3. make array with pointers to TFunctor, the base class, and initialize it
TFunctor* vTable[] = { &specFuncA, &specFuncB };
13