1-7) Functions and Function Pointers


C++ provides functions as well as pointers to funtions
This section provides a review of function basics and function pointers

[Return Type] funcName([argument list]) { code }  //note no semicolon

//example
double sqr(double y) {return y*y; }
void printOn(ostream & o) {o << name; }
foo(double x) {...}  //returns int by default
 


Functions can be prototyped (declared) and defined later;

C++ does not allow nested functions
Function’s return type can be void
Function can be exited prematurely using the return statement
A function must be delcared or defined before it is used, and must only be defined once
Defining braces { } don’t end in semicolon
A function can be declared using a protoype
Common practice:
    -declare function with prototype
    -code that uses function
    -definition of function

//program example: prototypes
#include <iostream.h>

//prototype of function
double sqr(double);    //prototype
main(){
for (int i = 0; i<5; i++)
cout << "\n i=" << i << "   i*i=" << sqr(i);   //use
};
double sqr(double x) {  //definition
  return x*x;
}


Return and Paramter Types
foo(BankAccount b) {...} //pass by value
foo(BankAccount *b) {...} //pass by pointer
foo(BankAccount &b) {...} //pass by reference

BankAccount deposit(float amt) {...} //return by value
BankAccount & deposit(float amt) {...} //return by reference
BankAccount * deposit(float amt) {...} //return by pointer



Memory Problem with Return by Reference
int & foo() {int i;
             i = 5}

void main() {
   int j;
   j = foo();
}

Do not return a reference to a local variable



Function Prototypes

Two options: list argument types only or types and parameter names

double volume(double, double);

double volume(double height, double radius);

Compiler ignores parmeter names --so be careful

//program example: works but confusing and error prone
#include <iostream.h>

//prototype of function
double vol(double height, double length , double wd);

main(){
double x=0, y=0, z=0;
cout << "Enter dimension l-w-h e.g. 3 2 5:";
cin >> x >> y >> z;
cout << "\n\nVolume is: " << vol(x, y, z);
};

double vol(double length, double width, double height) {
  return length * width * height;
}

Notice above the meaning of the parameters got switched, but this will not be caught by compiler



Scoping: What does this program output?

//example local variables
int i=10, y=2;
double silly(double);
main(){
cout << "silly returns: " << silly(y);
return 0;
};

double silly(double x) {
  int i = 5;
  return i*x;
}



Static Local Variables
Static local variables are initialized once, the first time the function is entered
They are like global variables and retain their value between successive function calls
They are only visible from within the function
 

void show_average(double x) {
 static double num = 0; //given an initial value
 static double sum = 0;
 num += 1;
 sum += x;
 cout << "\nnum=" << num << "  sum=" << sum << "  avg=" << sum/num;
}
 

main() {
double entry = 0;
for (;;) {
 cout << "\nEnter a number: ";
 cin >> entry;
 if (entry < 0) break;
 show_average(entry);
};
cout << "\nDone";
return 0;
}

//OUTPUT
Enter a number: 1
num=1 sum=1 avg=1

Enter a number: 2
num=2 sum=3 avg=1.5

Enter a number: 3
num=3 sum=6 avg=2

Enter a number: 4
num=4 sum=10 avg=2.5

Enter a nunber: -1
Done



Default Argument Values

C++ Allows default function arguments
Defaults are values which will be applied to arguments if the function call does not supply them
Some restrictions apply

//default argments example
#include <iostream.h>
#include <math.h>
double dist( double x2, double y2,
     double x1 = 0, double y1 = 0) {
 return sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
}
main()
{
 double px, py, qx, qy;
 cout <<"\nEnter point1 e.g. x y: ";
 cin >> px >> py;
 cout <<"\nEnter point1 e.g. x y: ";
 cin >> qx >> qy;

 cout  << "\ndist p1 to origin =" << dist( px, py);
 cout  << "\ndist p2 to origin =" << dist( qx, qy);
 cout  << "\ndist p1 to p2     =" << dist( px, py, qx, qy);
  return 0;
}

Once you assign a default value to a parameter you must do so for all subsequent parameters in the function

You must provide an arg. for all parameters that don’t have defaults

You may omit an argument if the parameter has a default

Once you omit the argument for a parameter with a default you must also omit omit all subsequent arguments (the defaults are used)

//Example 2
double dist( double x2, double y2,   double x1 = 0, double y1 = 0) {
 return sqrt((x2-x1)*(x2-x1) + (y2-y1)*(y2-y1));
}
main(){
 double px, py, qx, qy;
 //...
  cout << "\ndist p1 to origin =" << dist( px, py);
 cout << "\ndist p2 to origin =" << dist( qx, qy);
 cout << "\ndist p1 to p2 =    " << dist( px, py, qx, qy);
  return 0;
}



In C all function arguments are passed by value (the value might be a pointer)

C++ provides another mechanism -passing by reference

A reference implicitly creates a pointer which is hidden from the programmer

A reference can be used like the thing it references (no pointer manipulation required)

But remember, unlike a pass-by-value, pass by reference can alter the original entity being referred to.



Const Protected Reference Arguments
Reference arguments expose the original arguments to the function they are passed to
Declaring a reference argument as constant will prevent the function from modifying them

//constant reference argument example
#include <iostream.h>
struct personType {
  int age;
  char name[50];
};
void authorize(const personType &person) {
 if (person.age < 18)
  cout << "\nYou are too young to use this function";
 if (person.age > 21)
  person.age = 21;   //ERROR -CAUGHT BY THE COMPILER
}
main(){
 personType user;
 cout << "Enter your name and age: ";
 cin >> user.name >> user.age;
 authorize(user);
 return 0;
}
 



Function Overloading
Different functions can have the same name, but must be distinquished by their parameters, or constness.

//example: function overloading
#include <iostream.h>
void inc(int& i) {i = i+1;}
void inc(double& d) {d = d+1;}
void inc(char &c) {c = c+1;}
main()
{
 int i = 10;
 double pi = 3.14;
 char c = 'A';
 cout << i << ", " << pi << ", " << c;
 cout << "\nincremented:\n";
 inc(i);
 inc(pi);
 inc(c);
 cout << i << ", " << pi << ", " << c;
 cout << "\n";
 return 0;
}

Return Types are not examined in compile

//example2: function overloading
#include <iostream.h>
int silly() {return 42;}
char silly() {return 'A';} //Compile ERROR: Type mismatch
double silly() {return 3.14;}
main()
{
 int i;
 double x;
 char c;
 i = silly();
 x = silly();
 c = silly();
 cout << i << ", " << x << ", " << c;
 cout << "\n";
 return 0;
}



Pointers to Functions
You can take the address of a function. The invoking code then does not know what function it is invoking.
This is a very powerful feature. It is the basis for how dynamic binding (virtual functions) are implemented.

#include <iostream.h>
#include <string.h>

void sayAgain(const char * str, int n){
 for(int i=0; i<n; i++) cout << str << "\n"; }

void sayTimes(const char * str, int n){
 cout << str << " times " << n << "\n"; }

void saySpace(const char * str, int n){
 for(unsigned  i=0; i <  strlen(str); i++) {
  cout << str[i];
  for(int j=0; j<n; j++) cout << " ";
 }
 cout << "\n";
 
}

void doit(const char * string, int times, void (*f) (const char *, int)) {
  f(string, times);
}

void main(){

 doit("Hey", 3, &sayAgain);
 doit("Hello", 5, &sayTimes);
 doit("Bye", 5, &saySpace);

}
 

Output:
Hey
Hey
Hey
Hello times 5
B     y     e