| 1. | Introduction to C++ |
| 2. | Variables, branching, and loops |
| 3. | Functions and recursions |
| 4. | Pointers |
| 5. | Linked lists |
| 6. | Stacks |
| 7. | Sequences |
| 8. | Pointers practice |
| 9. | References |
| 10. | Sorting |
| 11. | Object oriented programming |
| 12. | Trees in C++ |
| 13. | Balanced trees |
| 14. | Sets and maps: simplified |
| 15. | Sets and maps: standard |
| 16. | Dynamic programming |
| 17. | Vectors |
| 18. | Multi core programming and threads |
| 19. | Representation of integers in computers |
| 20. | Floating point representation |
| 21. | Templates |
| 22. | Inheritance |
| 23. | Pointers to functions |
23. Pointers to Functions
1. Introduction
Pointers to functions are available in both C and C++. However, in C++, everything that can be achieved using function pointers can be accomplished using better alternatives. Templates, inheritance, and virtual methods provide more readable and maintainable code than function pointers.
The main concept and features are easy to understand; hence this chapter will be short. The problem with function pointers is not their complexity, but the complexity of the code that we write if we use function pointers and actively avoid the elegant alternatives that C++ provides.
Here is one example where function pointers can make the code shorter. The example is the sorting algorithm. Traditionally, we would be writing one function for sorting elements in non-decreasing order, and then another function for sorting in non-increasing order. Function pointers offer a possibility of writing only one sorting function. We can make two functions smallerNumber and biggerNumber. Each of these functions takes two parameters, and does what its name suggests. Then, the general sorting function takes function pointer, which will only later be specified to point to either smallerNumber or biggerNumber.
2. Addresses of functions
The functions are stored in memory, just like objects and variables. Everything that is located in the memory, has its address. Therefore, we can ask the program to print the address of the function.
#include<iostream>
long addNumbers(long a, long b){
return a+b;
}
int main(){
std::cout<<(long)(&addNumbers)<<"\n";
return 0;
}
During each execution of the program, we will see a different address. This behavior is very similar to what we experienced with addresses of variables.
3. Pointer variables
We now want to assign the address of a function to a variable. The variable must have the correct type, in order to receive the address. The type that we need is a function pointer. Let us define a function pointer aFun that is capable of storing the address for our function addNumbers. The declaration is shown below:
long (*aFun) (long, long);
The first word has to be the return type of the function. Then, we must give the name of the variable. The name of the pointer variable must be prefixed with * and must be in brackets. Finally, we must list the types of the function parameters. The following program uses our newly declared function pointer.
#include<iostream>
long addNumbers(long a, long b){
return a+b;
}
int main(){
std::cout<<(long)(&addNumbers)<<"\n";
long (*aFun) (long,long);
aFun=&addNumbers;
std::cout<<(long)(aFun)<<"\n";
return 0;
}
4. Dereferencing function pointers
We can use the pointer variables to call the functions. The languages C and C++ provide two ways to dereference function pointers and use them to call the functions. The first way is the same as was done with regular pointers.
std::cout<<"Calling the function: "<< (*aFun)(3,7)<<"\n";
An alternative way is to omit the dereference operator *. The following instruction will produce the same output
std::cout<<"Calling the function: "<< aFun(3,7)<<"\n";
Here is an entire program that illustrates the two ways to dereference the function pointer.
#include<iostream>
long addNumbers(long a, long b){
return a+b;
}
int main(){
long (*aFun) (long,long);
aFun=&addNumbers;
std::cout<<"First call to the function: "<<(*aFun)(3,7)<<"\n";
std::cout<<"Second call to the function: "<<aFun(3,7)<<"\n";
return 0;
}
Let us consider on simple example.
The functions addNumbers and multiplyNumbers are implemented below.
long addNumbers(long a, long b){
return a+b;
}
long multiplyNumbers(long a, long b){
return a*b;
}
The user input consists of three elements: Integer a, sign + or *, and the integer b. We will now use the function pointers to evaluate the correct function and print the result.
#include<iostream>
long addNumbers(long a, long b){
return a+b;
}
long multiplyNumbers(long a, long b){
return a*b;
}
int main(){
long a,b; char operation;
long (*aF)(long,long);
std::cin>>a>>operation>>b;
if(operation=='+'){
aF=&addNumbers;
}
else{
aF=&multiplyNumbers;
}
std::cout<<(*aF)(a,b)<<"\n";
return 0;
}
5. Function pointers as parameters of functions
We will now make a separate function that has a parameter which is a function pointer. We will use the same functions addNumbers and multiplyNumbers that we made before.
#include<iostream>
long addNumbers(long a, long b){
return a+b;
}
long multiplyNumbers(long a, long b){
return a*b;
}
void printResult(long a, long b, long (*aF)(long,long)){
std::cout<<"The result of the operation is "<<(*aF)(a,b)<<"\n";
}
int main(){
long a,b; char operation;
long (*aF)(long,long);
std::cin>>a>>operation>>b;
if(operation=='+'){
aF=&addNumbers;
}
else{
aF=&multiplyNumbers;
}
printResult(a,b,aF);
return 0;
}