Beginners to C++ programming find the concept of pointers foreign to them. Pointers, and code that manipulates pointers (aka pointer arithmetic), can even be daunting to some of the most seasoned programmers. Pointers in C++ account for a fair share of bugs in programs due to the lack of understanding. So why even use them? Well, besides being the most problematic, they are also some of the most powerful features in the language. Pointers make it quick to point to large objects in memory while they themselves are small. You could have a large stack of objects in memory and change a pointer to point to each one individually without wasting time copying the large objects around in memory.
This article is going to assume you have the concept of a variable pointer already down. What you may not know is that when you call a function using its name, you too are using another kind of pointer. These types of pointers point to locations of functions. Again, a memory location but instead of a variable value, we are pointing to a function out there in memory. Let’s talk about a function call…
printHello();
In the code above we are calling a function named “printHello”. The actual word “printHello” is a pointer to a function. When we make the call, we use this “printHello” pointer to find the function in memory, execute it and giving it any parameters we put in between the parenthesis (in this case none). Where variable pointers hold memory addresses of other variables, here this pointer holds the address of a function to execute.
C++ lets you create these pointers and then assign functions to them. Once you make the connection between the pointer and the function, you can call that function using the pointer. Now you may be asking “What in the hell is that good for?” The answer is “A few things like assigning user defined functions or passing around functions like a variable.” The second question you may be asking is “Is this a common thing to do?” and the answer is “Not really common… consider it a fringe or niche concept.”
Let’s take a look at a few examples of what we might be able to do with this concept and how to set it up…
#include <iostream> #include <string> using namespace std; void printAttack(); void printDefend(); int main() { // Function pointer called "printStatement" // It can point to functions which return void and takes no parameters void (*printStatement)(); // Assign each function to this pointer, use pointer to call them. printStatement = printAttack; printStatement(); printStatement = printDefend; printStatement(); // Above is implicit version (some older compiler may not support it) // Below again call but with explicit version // Dereference pointer and then use it to call printDefend again (*printStatement)(); return 0; } // Notice these two functions return nothing and take no parameters // Which matches our function pointer signature. void printAttack() { cout << "Ah Ha! En garde!" << endl; } void printDefend() { cout << "I thwart your attack dear sir!" << endl; }
This contrived example above is made ridiculously simple just to show how this works. In addition to main(), I have defined two standard printing statements. One for printing an attack statement and another to print a defend statement. Sword fighting game perhaps? 😉
In main you will see I created a function pointer variable called “printStatement”. It is a function pointer saying that it can point to any function which has a matching signature. In this case the functions it can point to are those which have a return type of void and takes no parameters. Notice our two print functions, printAttack and printDefend, which both have a return type of void and take no parameters… they match the pointer. The syntax for our pointer is a bit funky because we use parentheses around *printStatement. This is because if we don’t, it will be evaluated as void *printStatement(), a function call that takes no parameters and returns a pointer.
In our example we assign each of the functions to this pointer and then use the pointer to call the associated function. Notice I can call both printAttack and printDefend through the same pointer. I just changed which function the pointer was pointing to. As a side note, there are two ways to call the associated function, implicitly and explicitly. I demonstrate both here. Can you create a third statement and give it to the same pointer then call it? Try it!
This example is nice, but you might be asking yourself “can I pass these pointers around to functions?” You sure can! This is where it really shines because you could essentially write a function that accepts a function pointer and have it call that function without ever knowing what it is really calling. This would give you a generic utility function that you can give you the ability to call a multitude of different functions. Perhaps functions that haven’t even been written into the program yet. Extensibility for the win!
Here is an example…
#include <iostream> #include <string> using namespace std; void printMoves(void (*printStatement)(string)); void printSomeText(string); int main() { // Function pointer called "printStatement" // It can point to functions which return void and takes a string parameter void (*printStatement)(string moveText); // Set the pointer to point to our function for printing // Notice that printSomeText matches the pointer signature printStatement = printSomeText; // Pass the pointer to our printMoves function printMoves(printStatement); return 0; } // Use the passed pointer to a function to call printSomeText // with different strings void printMoves(void (*printStatement)(string move)) { printStatement("Ah ha! En garde!"); printStatement("I thwart your attack dear sir!"); } // Very generic function, could be one of many that our pointer could // possibly point to. void printSomeText(string theMove) { cout << theMove << endl; }
We are introducing a few new things here. Again I have two functions. One of them is very generic taking in a string and printing it. The other one, printMoves, takes a parameter which is a function pointer. This function pointer can point to functions which return void and take one string parameter. So again in main() we create a function pointer and we assign it the function printSomeText (notice how the signature of printSomeText matches the function pointer). Then we pass that pointer to printMoves which uses the pointer and gives it a few statements to print… our attack and defend statements from earlier. But you know what? printMoves has no idea that it is calling printSomeText. It is just going to the memory location pointed to by printStatement and give it the text.
We could have just as easily created another function called “printMoreText” set the pointer to point to that instead and give it to printMoves and it would have called that function all the same. One situation we could give it printSomeText and in another give it printMoreText all without having to change printMoves.
If you are familiar with .NET, this concept is similar to delegates. They are essentially function pointers as well but with a little more flexibility. Here in C++ we could have the user create their own functions and give them to printMoves to invoke, we could have 10 different print functions that all have the same signature but do different things, printMoves would handle them all. Generic here is good and this sort of thing can be found in a ton of utility programs.
One last thing I want to mention about function pointers is the idea of simplifying the pointers down using typedef. Using typedef we could have cleaned up a little bit of the syntax with a statement like…
typedef void (*printStatement)(string);
Then anywhere we wanted to have a function pointer to point to functions that return void and takes a string parameter we would simply use “printStatement”….
void printMoves(printStatement func) { func("Ah ha! En garde!"); func("I thwart your attack dear sir!"); }
I think if you play with the two programs I provided above you will realize their flexibility and how you might use them. You may not use this concept every day, but at least you can impress your friends at dinner parties! That right there is worth its weight in free pizza. Thanks for reading! 🙂