Last modified: Tue, 07 Dec 2010 23:06:12 +0200
Complete: ##################-- (90%)
6.1 Introduction
6.2 Arguments passed by value and by reference
6.3 Default Parameters
6.4 Recursion
6.5 Overloading Functions (Polymorphism)
6.6 Macro Functions
6.7 Examples
6.8 Exercises
6.1 Introduction
In this section some slightly more advanced features of functions in C++ will be discussed.
6.2 Arguments passed by value and by reference
In section 5 we have seen that the arguments (function parameters) of functions are passed by value.
That is when we call a function with arguments we pass values to the function and not the variables themselves.
For example consider Program 6.1 below.
06prg01.cpp: Passing arguments by value
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
|
// Passing arguments by value
#include <iostream>
using namespace std;
void increment(int a, int b) {
a++; b++;
cout << "Inside increment(): " << a << '\t' << b << endl;
}
int main()
{
int x = 4, y = 7;
cout << "Before increment(): " << x << '\t' << y << endl;
increment (x,y); // has no affect on variable x and y!
cout << "After increment(): " << x << '\t' << y << endl;
return 0;
}
|
Before increment(): 4 7
Inside increment(): 5 8
After increment(): 4 7
|
Looking at the output, we see that the call to increment() has no affect on the values in the main program.
Variables a and b are local and have no reference to variables x and y. The function
increment() in this case useless.
In the next example, Program 6.2, the function increment() has been modified such that
the arguments are passed by reference. The only modification is the replacement
of int with int& in the function definition; the ampersand (&)
symbols define the passing of arguments by reference instead of by value.
06prg02.cpp: Passing arguments by reference
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
|
// Passing arguments by reference
#include <iostream>
using namespace std;
void increment(int& a, int& b) {
a++; b++;
cout << "Inside increment(): " << a << '\t' << b << endl;
}
int main()
{
int x = 4, y = 7;
cout << "Before increment(): " << x << '\t' << y << endl;
increment (x,y);
cout << "After increment(): " << x << '\t' << y << endl;
return 0;
} |
Before increment(): 4 7
Inside increment(): 5 8
After increment(): 5 8
|
In this case the modified values of a and b
are passed back to variables x and y in the main function; i.e. the result of the function call is to increment
variables x and y.
Note that there is some freedom in the placement of &; for example following two statements are equivalent.
void increment(int& a, int& b) {
and
void increment(int &a, int &b) {
A simple, but useful, example of passing arguments by reference is given in Program 6.3 below.
Here the swap(x,y) function swaps (exchanges) the values of two variables.
06prg03.cpp: Swapping two values
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
|
// Swapping two values
#include <iostream>
using namespace std;
void swap(int& x, int& y){
int z = x;
x = y;
y = z;
}
int main()
{
int x = 22, y = 33;
cout << "Values before swapping: " << x << " " << y << endl;
swap (x, y);
cout << "Values after swapping: " << x << " " << y << endl;
return 0;
} |
Values before swapping: 22 33
Values after swapping: 33 22
|
6.3 Default Parameters
A default value can be defined for each parameter of a function; if a parameter is not supplied then the default value is used.
The assignment operator together with a default value is used to specify the default arguments in the function decleration.
Program 6.4 provides an example:
06prg04.cpp: Default values in a function
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
|
// Default values in a function
#include <iostream>
using namespace std;
float cylinderVolume(float radius, float height = 2.5){
const float pi = 3.141593;
return pi * radius * radius * height;
}
int main()
{
float r = 1.1, h = 1.3;
cout << cylinderVolume(r) << endl;
cout << cylinderVolume(r, h) << endl;
return 0;
} |
9.50332
4.94173
|
In main(), there are two calls to the function cylinderVolume() which returns the volume of a cylinder
of known radius and height.
- In the first call cylinderVolume(r); the height parameter is not supplied and so the default value (height = 2.5) is used.
- In the second call: cylinderVolume(r, h); both parameters are supplied and so the default value is not used.
The the assignment operators have to be specifed in the prototype of a function having default arguments.
The following two prototypes are equivalent:
cylinderVolume(float radius, float height = 2.5);
cylinderVolume(float, float = 2.5);
Note that
The use of default arguments is optional, so they must all be listed last when declaring a function.
|
Lets now look at a more complicated example. Consider the third order polynomial function:
p(x) = a + bx + cx2 + dx3,
with the coefficients a, b, c, d are real constants.
A five-parameter (x, a, b, c, and d) function with default arguments can be implemented as shown in Program 6.5:
06prg05.cpp: A polynomial function with default arguments
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
|
// A polynomial function with default arguments
#include <iostream>
using namespace std;
double poly(double, double, double = 0., double = 0., double = 0.);
int main() {
double x;
cout << "Input x: ";
cin >> x;
cout << "2.3 - 5.4 x = "
<< poly(x,2.3,-5.4) << endl;
cout << "2.3 - 5.4 x + 1.4 x^2 = "
<< poly(x,2.3,-5.4,1.4) << endl;
cout << "2.3 - 5.4 x + 1.4 x^2 + 0.8 x^3 = "
<< poly(x,2.3,-5.4,1.4,0.8) << endl;
}
double poly(double x, double a, double b, double c, double d) {
return a + b*x + c*x*x + d*x*x*x;
} |
Input x: 1.5
2.3 - 5.4 x = -5.8
2.3 - 5.4 x + 1.4 x^2 = -2.65
2.3 - 5.4 x + 1.4 x^2 + 0.8 x^3 = 0.05
|
6.4 Recursion
A function can call itself. This is called recursion. Some problems are most easily solved by recursion.
To illustrate solving a problem using recursion, consider the function fact(n) that returns n! (n factorial) for n>0:
n! = 1 * 2 * 3 * ....* (n-1) * n
or alternatively (backwards)
n! = n * (n-1) * (n-2) * (n-3) ... * 1
which can be written recursively as
and 0! = 1.
In C++, a recursive function to calculate n! can be implemented as shown in Program 6.6:
06prg06.cpp: Recursive factorial function
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
|
// Recursive factorial function
#include <iostream>
using namespace std;
int fact (int n){
if (n > 1)
return n * fact(n-1);
else
return (1);
}
int main ()
{
int number;
cout << "Input a number: ";
cin >> number;
cout << number << "! = " << fact(number);
return 0;
} |
Input a number: 5
5! = 120
|
6.5 Overloading Functions (Polymorphism)
C++ enables you to generate more than one function with the same name.
The functions must differ in their parameter list with different types or different number of parameters, or both.
Each function can have different return type.
This is called function overloading. Function overloading is also known as function polymorphism (
Poly means many, and morph means form: a polymorphic function is many-formed).
Here are some example prototypes of a max() function which returns the maximum among the parameters.
int max(int, int); // two integer parameters
int max(int, int, int); // three integer parameters
double max(double, double); // two double parameters
double max(double, double, double); // three double parameters
An example usage of a max() function overloaded with types int and double
and with two and three parameters is given in Program 6.7.
06prg07.cpp: Overloaded max() function
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
|
// Overloaded max() functions
#include <iostream>
using namespace std;
int max(int x, int y) {
return (x>y ? x:y);
}
int max(int x, int y, int z) {
int m = (x>y ? x:y);
return (z>m ? z:m);
}
double max(double x, double y) {
return (x>y ? x:y);
}
double max(double x, double y, double z) {
double m = (x>y ? x:y);
return (z>m ? z:m);
}
int main()
{
cout << "max(9,7) = " << max(9,7) << endl;
cout << "max(3,6,2) = " << max(3,6,2) << endl;
cout << "max(3.1,4.7) = " << max(3.1,4.7) << endl;
cout << "max(3.1,9.3,4.7) = " << max(3.1,9.3,4.7) << endl;
return 0;
} |
max(9,7) = 9
max(3,6,2) = 6
max(3.1,4.7) = 4.7
max(3.1,9.3,4.7) = 9.3
|
Many intrinsic functions are overloaded, for example sin(x) will return a float or a double or a long double depending on the type of the argument x.
Note that
To use an overloaded function, a function must be available that matches the number of parameters
and types of parameters in the call. For example the overloaded functions above will not support
the following the call max(3,6.4,2) as this would require an additional double max(int, double, int) function to be defined.
|
6.6 Macro Functions
In the header files (such as iostream, cmath, cstdlib), you can see some definitions of
macro functions. A macro function, which is actually not a function, is defined by using #define preprocessor command.
The following macro function definitions are valid in C++.
#define square(x) (x)*(x)
#define hypotenus(x,y) sqrt((x)*(x)+(y)*(y))
#define delta(a,b,c) ((b)*(b)-4*(a)*(c))
#define max(a,b) ((a>b) ? a:b)
In the definition of max(a,b) for example, any occurrence of max() is replaced the expression (a>b) ? a:b
but also replacing each argument by the supplied identifiers.
The use of this kind of function is exactly the same as the other functions, but they must be defined at the begining of a program.
Note that the use of paranthesis in the macro function definition is important since an expression can be replaced with arguments.
For example the macros div1() and div2() below will generally not give the same result:
#define div1(x,y) x/y
#define div2(x,y) (x)/(y)
...
y1 = div1(a+b, 2); // equivalent to y1 = a+b/2;
y2 = div2(a+b, 2); // equivalent to y2 = (a+b)/(2);
An example application is given in Program 6.8 that uses the macro hypotenus(x,y) to find Pythagorian Triples ≤ 50.
A Pythagorian triple denoted by (a, b, c) consists of three positive integers a, b, and c, such that
a2 + b2 = c2. For example (3, 4, 5) is Pythagorian triple since
32 + 42 = 52.
06prg08.cpp: Finding Pythagorian Triples
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
|
// Finding Pythagorian Triples
#include <iostream>
#include <cmath>
using namespace std;
// macro function definition
#define hypotenus(x,y) sqrt((x)*(x) + (y)*(y))
#define N 50
int main ()
{
int a, b, c;
cout << "Pythagorean Triples less than N = 50" << endl;
for (a=1; a<=N; a++)
for (b=a; b<=N; b++)
for (c=1; c<=N; c++)
if( c == hypotenus(a,b) )
cout << "("
<< a << ", " << b << ", " << c
<< ")" << endl;
return 0;
} |
Pythagorean Triples less than N = 50
(3, 4, 5)
(5, 12, 13)
(6, 8, 10)
(7, 24, 25)
(8, 15, 17)
(9, 12, 15)
(9, 40, 41)
(10, 24, 26)
(12, 16, 20)
(12, 35, 37)
(14, 48, 50)
(15, 20, 25)
(15, 36, 39)
(16, 30, 34)
(18, 24, 30)
(20, 21, 29)
(21, 28, 35)
(24, 32, 40)
(27, 36, 45)
(30, 40, 50)
|
6.7 Examples
06prg09.cpp: Example of passing arguments by reference
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
|
// Example of passing arguments by reference
// The Cartesian coordinates (x, y, z) of a point are
// obtain from the spherical coordinates (r, theta, phi).
// Note that only x, y, and z are passed by reference.
#include <iostream>
#include <cmath>
using namespace std;
void getCartesianCoordinates(
double, double, double, double&, double&, double&);
int main() {
double r, theta, phi;
cout << "Input coordinates r, theta, and phi: ";
cin >> r >> theta >> phi;
double x, y, z;
getCartesianCoordinates(r, theta, phi, x, y, z);
cout << x << " " << y << " " << z << endl;
}
void getCartesianCoordinates(
double r, double t, double p, double& a, double& b, double& c)
{
a = r*sin(t)*cos(p);
b = r*sin(t)*sin(p);
c = r*cos(t);
} |
Input coordinates r, theta, and phi:
125 1.5707963267949 0.0
125 0 -4.36435e-13
|
06prg10.cpp: Example of a default value in a function
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
|
// Example of a default value in a function
// The change in potential energy of a body is returned by
// a function given the mass(m), change in height(h), and
// acceleration due to gravity(g). Passing the value of
// g is optional, the default value being 9.81.
#include <iostream>
using namespace std;
double dP(double m, double h, double g = 9.81) {
return m*g*h;
}
int main()
{
cout << dP(1.5,25.,9.81) << endl;
cout << dP(1.5,25.) << endl;
cout << dP(1.5,25.,6.23) << endl;
} |
367.875
367.875
233.625
|
06prg11.cpp: Comparison of passing by value and by reference
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
|
// Comparison of passing by value and by reference
#include <iostream>
#include <cmath>
#include <string>
using namespace std;
// prototypes
int bacteriaGrowthbyValue(int, double);
void bacteriaGrowthbyRefer(int, double, int&);
int main() {
int n, m;
n = bacteriaGrowthbyValue(100, 3600.*6.);
cout << "Number of bacteria = " << n << endl;
// m is passed by reference
bacteriaGrowthbyRefer(100, 3600.*6., m);
cout << "Number of bacteria = " << m << endl;
}
// a function with values passed by value
int bacteriaGrowthbyValue(int initialBacteria,
double growthTime)
{
const double growthRate = 2.8e-4;
return int(initialBacteria*exp(growthRate*growthTime));
}
// a function with a value passed by reference
void bacteriaGrowthbyRefer(int initialBacteria,
double growthTime,
int &bacteria)
{
const double growthRate = 2.8e-4;
bacteria = int(initialBacteria*exp(growthRate*growthTime));
} |
Number of bacteria = 42326
Number of bacteria = 42326
|
6.8 Exercises
- What is the difference between arguments passed by value and by reference?
- What is recursion?
- What is function overloading and polymorphism?
- What is the difference between a macro function and an ordinary function?
- Write a recursive function called unsigned fib(unsigned n) that returns the n'th element of the Fibonacci series:
1, 1, 2, 3, 5, 8, 13, 21, 34, ...
Each number, after the second, is the sum of the two numbers before it.
- Write a program that inputs an integer number n and an integer power p
and outputs n p. The calculation should be done using a recursive function
named int power(n, p).
- What do you expect to be the output of the following program?
Compile and run the program, is the output what you expected? If not, explain why.
#include <iostream>
int foo (int =1);
double foo (double);
int main(void)
{
for(int i=0; i<20; i+=3){
int x = foo();
int y = foo(i-x);
double z = foo(1.0*i);
std::cout << x << '\t' << y << '\t' << z << std::endl;
}
return 0;
}
int foo (int a){
if (a > 1) return (a * foo (a-1));
else return 1;
}
double foo (double a){
if (a > 1.0) return (a * foo (a-1.0));
else return 1.0;
}
|