|

Pointers may also be declared to point
to functions. It is interesting to note that function names are constant pointers
to the function itself. You can declare a pointer variable that points to a function. The function can then be invoked
by using that pointer.
This allows programs to dynamically decide which functions to invoke based
upon user input.
When using function pointers, you must be clear about the type of object pointed to
- so watch your data types and
return types!. A pointer to a function must point to a function of the appropriate return type and signature. Example:
long (* funcPtr) (int);
| "The
reasonable man adapts himself to the world; the
unreasonable one persists in trying to adapt the
world to himself. Therefore, all progress depends on
the unreasonable man."
- George Bernard Shaw |
In the example above, "funcPtr" is declared to be a pointer that points to a function that takes an
int parameter and returns a
long. Parentheses are necessary around "*funcPtr" because the parentheses around "int" bind tighter and have a higher precedence than the indirection operator "*". Without the
first set of parenthesis, the previous statement would declare a function,
rather than a pointer to a function, that takes and integer and returns a pointer to a long. Declaration of a function pointer always includes
the return type and parentheses, indicating the type of parameters.
//Using function pointers
#include <iostream.h>
//Function prototypes
void Square (int&,int&);
void Cube (int&, int&);
void Swap (int&, int &);
void GetVals(int&, int&);
void PrintVals(int, int);
enum BOOL { FALSE, TRUE };
int main()
{
void (*
AFunctionPointer) (int &,
int &);
BOOL fQuit = FALSE;
int valOne=1, valTwo=2;
int choice;
while(fQuit == FALSE)
{
cout << "(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: ";
cin >> choice;
switch(choice)
{
case 1:
AFunctionPointer = GetVals; break;
case 2:
AFunctionPointer = Square; break;
case 3:
AFunctionPointer = Cube; break;
case 4:
AFunctionPointer = Swap; break;
default : fQuit = TRUE;
break;
}
if(fQuit)
break;
PrintVals(valOne, valTwo);
AFunctionPointer(valOne, valTwo);
PrintVals(valOne, valTwo);
} //close
while true loop
return 0;
}
void PrintVals(int x,
int y)
{
cout << "x: " << x << " y: " << y << endl;
}
void Square(int & rX,
int & rY)
{
rX *= rX;
rY *= rY;
}
void Cube(int & rX,
int & rY)
{
//Could replace with rx = rx * rx * rx.
int tmp;
tmp = rX;
rX *= rX;
rX = rX * tmp;
tmp = rY;
rY *= rY;
rY = rY * tmp;
}
void Swap(int & rX, int & rY)
{
int temp;
temp = rX;
rX = rY;
rY = temp;
}
void GetVals(int & rValOne,
int & rValTwo)
{
cout << "New value for ValOne: ";
cin >> rValOne;
cout << "New value for ValTwo: ";
cin >> rValTwo;
} |
Output:
(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 1
x:1 y:2
New Value for ValOne: 2
New Value for ValTwo: 3
x:2 y:3
(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 3
x:2 y:3 |
x:8 y:27
(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 2
x:8 y:27
x:64 y:729
(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 4
x:64 y:729
x:729 y:64
(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: 0 |
Four functions are declared in this
example with same return type and signature, returning void and taking 2 references to integers.
Next, "AFunctionPointer" is declared to be pointer to
a function that returns void and takes 2 integer reference parameters. Any of previous functions can be pointed to by
AFunctionPointer, since they all take two int
references and return void.
Remember that they must agree in both their argument types and return
type in order for the function pointer to work properly.
The user is offered the choice about which functions to invoke, and "AFunctionPointer" is assigned accordingly
to a function. This has the effect of invoking the function that
AFunctionPointer points to the next time we use this pointer as a
function call. In the PrintVal
function, the current value of two int's is printed,
the currently assigned function is invoked, and then the values are printed again.
In this case, we are dealing with memory addresses rather than values,
so pointers to functions do not need to be dereferenced. If "AFunctionPointer" is a pointer to a function, taking an integer and returning a variable of type long, and you
then assign AFunctionPointer to a matching function, you can invoke it 2 ways:
AFunctionPointer(x); - shorthand version, not dereferenced
(*AFunctionPointer)(x);
- long version, dereferenced.
Arrays of pointers to functions -
You can declare arrays of pointers to functions for returning specific value types and signatures:
//Demonstrates use of an array of pointers to functions
#include <iostream.h>
void Square (int&,int&);
void Cube (int&, int&);
void Swap (int&, int &);
void GetVals(int&, int&);
void PrintVals(int, int);
enum BOOL { FALSE, TRUE };
int main()
{
int valOne=1, valTwo=2;
int choice, i;
const MaxArray = 5;
void (*FunctionPointerArray[MaxArray])(int
&, int &);
for(i=0;i<MaxArray;i++)
{
cout << "(1)Change Values (2)Square (3)Cube (4)Swap: ";
cin >> choice;
switch(choice)
{
case 1:
FunctionPointerArray[i] = GetVals;
break;
case 2:
FunctionPointerArray[i] = Square;
break;
case 3:
FunctionPointerArray[i] = Cube;
break;
case 4:
FunctionPointerArray[i] = Swap;
break;
default:
FunctionPointerArray[i] = 0;
}
} //close for
loop
for(i=0;i<MaxArray; i++)
{
FunctionPointerArray[i](valOne,valTwo);
PrintVals(valOne,valTwo);
}
return 0;
}
void PrintVals(int x,
int y)
{
cout << "x: " << x << " y: " << y << endl;
}
void Square(int & rX,
int & rY)
{
rX *= rX;
rY *= rY;
}
void Cube(int & rX,
int & rY)
{
int tmp;
tmp = rX;
rX *= rX;
rX = rX * tmp;
tmp = rY;
rY *= rY;
rY = rY * tmp;
}
void Swap(int & rX,
int & rY)
{
int temp;
temp = rX;
rX = rY;
rY = temp;
}
void GetVals(int & rValOne,
int & rValTwo)
{
cout << "New value for ValOne: ";
cin >> rValOne;
cout << "New value for ValTwo: ";
cin >> rValTwo;
} |
Output:
(1)Change Values (2)Square (3)Cube (4)Swap: 1
(1)Change Values (2)Square (3)Cube (4)Swap: 2
(1)Change Values (2)Square (3)Cube (4)Swap: 3
(1)Change Values (2)Square (3)Cube (4)Swap: 4
(1)Change Values (2)Square (3)Cube (4)Swap: 2 |
New Value for ValOne: 2
New Value for ValTwo: 3
3 x:2 y:3
x:4 y:9
x:64 y:729
x:729 y:64
x:7153 y:4096 |
Passing pointers of functions to other functions -
Pointers to functions can be passed to other functions that take action:
//Function pointers
passed to other functoins
#include <iostream.h>
void Square (int&,int&);
void Cube (int&, int&);
void Swap (int&, int &);
void GetVals(int&, int&);
void PrintVals(void (*)(int &, int &),int &, int &);
enum BOOL { FALSE, TRUE };
int main()
{
int valOne=1, valTwo=2;
int choice;
BOOL fQuit = FALSE;
void (*
FunctionPointer )(int&, int&);
while(fQuit == FALSE)
{
cout << "(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: ";
cin >> choice;
switch(choice)
{
case 1: FunctionPointer = GetVals;
break;
case 2: FunctionPointer = Square;
break;
case 3: FunctionPointer = Cube;
break;
case 4: FunctionPointer = Swap;
break;
default: fQuit = TRUE;
break;
}
if(fQuit == TRUE)
break;
PrintVals(FunctionPointer, valOne, valTwo);
} //close while
true
return 0;
}
void PrintVals(
void (*FunctionPointer)(int
&, int &),int
& x, int & y)
{
cout << "x: " << x << " y: " << y << endl;
FunctionPointer(x,y);
cout << "x: " << x << " y: " << y << endl;
}
void Square(int & rX,
int & rY)
{
rX *= rX;
rY *= rY;
}
void Cube(int & rX,
int & rY)
{
int tmp;
tmp = rX;
rX *= rX;
rX = rX * tmp;
tmp = rY;
rY *= rY;
rY = rY * tmp;
}
void Swap(int & rX,
int & rY)
{
int temp;
temp = rX;
rX = rY;
rY = temp;
}
void GetVals (int & rValOne,
int & rValTwo)
{
cout << "New value for ValOne: ";
cin >> rValOne;
cout << "New value for ValTwo: ";
cin >> rValTwo;
} |
The expression,
void PrintVals(void (*)(int&, int&),int&, int&;
, may save the program when it is a required construct, but this should
be used infrequently. Also, the expression
void (*)(int&, int&) is cumbersome.
We can use typedef to simplify this:
//Function pointers
with typedef
#include <iostream.h>
void Square (int&,int&);
void Cube (int&, int&);
void Swap (int&, int &);
void GetVals(int&, int&);
//Set up FUNC as a pointer to a function that takes
two references as arguments.
typedef
void (*FUNC) (int &, int&);
void PrintVals(FUNC, int&, int&);
enum BOOL { FALSE, TRUE };
int main()
{
int valOne=1, valTwo=2;
int choice;
BOOL fQuit = FALSE;
FUNC
FunctionPointer;
while(fQuit == FALSE)
{
cout << "(0)Quit (1)Change Values (2)Square (3)Cube (4)Swap: ";
cin >> choice;
switch(choice)
{
case 1: FunctionPointer = GetVals;
break;
case 2: FunctionPointer = Square;
break;
case 3: FunctionPointer = Cube;
break;
case 4: FunctionPointer = Swap;
break;
default: fQuit = TRUE;
break;
}
if(fQuit == TRUE)
break;
PrintVals(FunctionPointer, valOne, valTwo);
} //close while
true
return 0;
}
void PrintVals(FUNC
FunctionPointer, int & x, int & y)
{
cout << "x: " << x << " y: " << y << endl;
FunctionPointer(x,y);
cout << "x: " << x << " y: " << y << endl;
}void Square(int & rX,
int & rY)
{
rX *= rX;
rY *= rY;
}
void Cube(int & rX,
int & rY)
{
int tmp;
tmp = rX;
rX *= rX;
rX = rX * tmp;
tmp = rY;
rY *= rY;
rY = rY * tmp;
}
void Swap(int & rX,
int & rY)
{
int temp;
temp = rX;
rX = rY;
rY = temp;
}
void GetVals (int
& rValOne, int & rValTwo)
{
cout << "New value for ValOne: ";
cin >> rValOne;
cout << "New value for ValTwo: ";
cin >> rValTwo;
} |
lIn the example above,
typedef is used to declare
FUNC to be a function that returns void and takes
two parameters which are both integer references. The object, "FunctionPointer"
is then declared to be of type FUNC.
Pointers to member functions - Unlike pointers to functions, pointers to member functions REQUIRE an object of the class on which to invoke them.
There must be an instance. Use same syntax as a pointer to a function, but INCLUDE the class name and the scoping operator (::).
For example: if FunctionPointer points to a member function of the class Shape which takes
two integers and returns void, we would write the expression:
void (Shape::*FunctionPointer) (int, int); Example:
//Pointers to
class member functions
#include <iostream.h>
enum BOOL {FALSE, TRUE};
class Mammal
{
public:
Mammal():itsAge(1) { }
~Mammal() { }
virtual void Speak() const =
0;
virtual void Move() const =
0;
protected:
int itsAge;
};
class Dog :
public Mammal
{
public:
void Speak() const
{ cout << "Woof!\n"; }
void Move()
const { cout << "Walking to heel...\n"; }
};
class Cat :
public Mammal
{
public:
void Speak()
const { cout << "Meow!\n"; }
void Move()
const { cout << "slinking...\n"; }
};
class Horse :
public Mammal
{
public:
void Speak()
const { cout << "Winnie!\n"; }
void Move()
const { cout << "Galloping...\n"; }
};
int main()
{
//FunctionPointer
points to a Mammal function.
void (Mammal::*FunctionPointer)()
const = 0;
Mammal * ptr = 0;
int Animal;
int Method;
BOOL fQuit = FALSE;
while(fQuit == FALSE)
{
cout << "(0)Quit (1)dog (2)cat (3)horse: ";
cin >> Animal;
switch(Animal)
{
case 1: ptr =
new Dog;
break;
case 2: ptr =
new Cat;
break;
case 3: ptr =
new Horse;
break;
default: fQuit = TRUE;
break;
}
if(fQuit)
break;
cout << "(1)Speak (2)Move: ";
cin >> Method;
switch(Method)
{
case 1: FunctionPointer = Mammal::Speak;
break;
default: FunctionPointer = Mammal::Move; break;
}
(ptr->*FunctionPointer)();
delete ptr;
} //close while
true loop
return 0;
} |
Output:
(0)Quit (1)Dog (2)Cat (3)Horse: 1
(1)Speak (2)Move: 1
Woof!
(0)Quit (1)Dog (2)Cat (3)Horse: 2
(1)Speak (2)Move: 1
|
Meow!
(0)Quit (1)Dog (2)Cat (3)Horse: 3
(1)Speak (2)Move: 2
Galloping
(0)Quit (1)Dog (2)Cat (3)Horse: 0 |
In this example, new sub classes of
the base class, Mammal, are created on free store and assigned to ptr.
The class member method chosen and assigned to FunctionPointer is invoked by the object created.
The "ptr" accesses the object and "FunctionPointer" accesses the function.
Even though this points to the Mammal function, the methods were
declared virtual and so have been overridden in every derived class.
In this way, they simpy "do the right thing". There is NO need to call delete on
the function pointer "FunctionPointer" because it is a pointer to code, not to an object on the free store.
However, we do need to call delete on "ptr", since it points to actual
Mammal objects created on the heap using "new".
Arrays of pointers to member functions - pointers to member functions can be stored in an array:
//An
array of pointers to class member functions.
#include <iostream.h>
enum BOOL {FALSE, TRUE};
class Dog
{
public:
void Speak()
const { cout << "Woof!\n"; }
void Move()
const { cout << "Walking to heel...\n"; }
void Eat()
const { cout << "Gobbling food...\n"; }
void Growl()
const { cout << "Grrrrr\n"; }
void Whimper()
const { cout << "Whining noises...\n"; }
void RollOver()
const { cout << "Rolling over...\n"; }
void PlayDead()
const
{ cout << "Is this the end of Little Caeser?\n"; }
};
//Sets up PDF to be a pointer to a Dog function.
typedef void (Dog::*PDF)()const ;
int main()
{
const int MaxFuncs = 7;
//Create an array of pointers to Dog class
functions.
PDF DogFunctions[MaxFuncs] =
{ Dog::Speak,
Dog::Move,
Dog::Eat,
Dog::Growl,
Dog::Whimper,
Dog::RollOver,
Dog::PlayDead };
//Create a pointer to a dog object.
Dog * pDog =
0;
int Method;
BOOL fQuit = FALSE;
while (!fQuit)
{
cout << "(0)Quit (1)Speak (2)Move (3)Eat (4)Growl";
cout << " (5)Whimper (6)Roll Over (7)Play Dead: ";
cin >> Method;
if
(Method == 0)
{
fQuit = TRUE;
break;
}
else
{
pDog = new Dog;
//Create a new Dog on the heap. Point to it.
(pDog->*DogFunctions[Method-1])();
delete pDog;
}
} //close
while true loop
return 0;
} |
Output:
(0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)Play Dead: 1
Woof!
(0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)Play Dead: 4
Grrr
(0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)Play Dead: 7
Is this the end of Little Ceasar?
(0)Quit (1)Speak (2)Move (3)Eat (4)Growl (5)Whimper (6)Roll Over (7)Play Dead: 0
In this example, the Dog class is created with 7 member functions sharing
the same return type and signature. The typedef declares
PDF to be a pointer to a member function of Dog
class, that takes no parameters and returns no values, and that is
const (in agreement with the signature of the 7 member functions of Dog).
We then create an array of pointers to Dog class functions, "DogFunctions", to hold
the seven member functions and to be initialized with their addresses.
The user prompted to pick a method. Unless "Quit" is selected, a new Dog object is created on heap and
the correct method is invoked in the array.
Analyze this: (pDog->*DogFunctions[Method - 1])();
(good for a table built from member functions).
©2004 C. Germany
|