·
Predefined
Functions – Functions that come out of the C++ Libraries. Make sure you include the header file that
has the function prototype (declaration).
o
Predefined
Functions That Return a Value – A function that performs an action and returns
a value.
#include <iostream>
#include <cmath>
using namespace std;
int main()
{
double result, a = 2, b = 3;
result = pow(a,
b);
cout << “Result = ”
<< result << endl;
return 0;
}
o
Predefined void
Functions – A function that performs an action, but does not return a value.
#include <iostream>
#include <cstdlib>
using namespace std;
int main()
{
cout << “Exiting with an
error code.\n”;
exit(0x2ab);
cout << “We will never reach
this point in the code.\n”;
return 0;
}
·
Programmer-Defined
Functions – These are functions that are defined by the programmer. The declarations for the functions should be
placed into a .hpp header
file.
o
Defining Functions
that Return a Value
// Generic prototype that is put into a header file
<return type> functionName(<param_type> param1, <param_type
param2, etc …);
// A sample function prototype (located in interest.hpp file)
double interestEarned(double principal, double rate, int numberYears);
// The function implementation (located in interest.cpp file)
#include <iostream>
#include <cmath>
using namespace std;
////////////////////////////////////////////////////////////////////////////////
// interestEarned()
//
// Description: This function will return the interest
// on a loan or investment for a given period of time.
// The formula used for calculating the compounded
// interest is based upon the following conditions:
//
// P = original principal amount
// I = annual interest rate (in decimal form)
// N = number of compounding periods per year
// Y = number of years
// T = total of principal and interest to date
//
// The general formula for compound interest calculation
// can be written as
//
// T = P ( 1 + I / N )^YN
//
// Inputs: principal
// rate
// numberYears
//
// Outputs: none
//
// Return: interest
/////////////////////////////////////////////////////////////////////////////////
double interestEarned(double principal, double rate, int numberYears)
{
double totalAmount, interest;
totalAmount = principal * pow((1 + (rate/100)),
static_cast<double>(numberYears));
interest = totalAmount - principal;
return interest;
}
// The driver function implementation (located in example.cpp)
#include <iostream>
#include "interest.h"
using namespace std;
int main()
{
double principal, rate, interest;
int numberYears;
cout << "Enter principal, interest rate, and number of years: ";
cin >> principal >> rate >> numberYears;
interest = interestEarned(principal, rate, numberYears);
cout.setf(ios::fixed); // Floats as fixed points
cout.setf(ios::showpoint); // Always show decimal point
cout.precision(2); // Number decimal digits
cout << "You've earned $" << interest << " in interest.\n";
return 0;
}
o
Alternate Form
for Function Prototype
Be
aware that both function declarations may have the variable identifier or may
simply have the type (no identifier).
double interestEarned(double principal, double rate, int numberYears);
or
double interestEarned(double, double, int);
This
is only true for the function prototype (declaration). Formal parameters must be listed in the
actual function definition.
o
Functions Calling
Functions – When functions call other
functions, whether user-defined or not, it is important that the calling
function know the function prototype or function
definition of the function it is calling.
o
Functions that Return a bool Value – A function
that may take formal parameters, but returns a Boolean true/false value to the
calling function.
#include <iostream>
using
namespace std;
bool checkRange(int value, int min, int max)
{
if((value >= min) && (value <= max))
return true;
else
return false;
}
int main()
{
int value;
cout << "Enter a value:
";
cin >> value;
if(checkRange(value, 0, 100))
cout << "In
range.\n";
else
cout << "Out of
range.\n";
return 0;
}
o
Defining void
Functions – Void functions may take formal parameters but do not return a
value. The call to the void function is
coded on a single line much like a statement and is not used in arithmetic
expressions as a variable or non-void function might be.
#include <iostream>
using
namespace std;
void displayCopyright()
{
cout << "Copyright (c) 2002\n"
<<
"All rights Reserved\n\n";
}
int main()
{
displayCopyright();
return 0;
}
o
Return Statements
in void Functions – A return statement may be placed in a void function if it
is desirable to leave the function for a given condition. The return statement is written on a line by
itself without anything to return:
void someFunction(int arg1)
{
int number;
// do some stuff
if(!number)
return;
// do some more stuff if number was found to be non-zero
}
o
main Function – The main() function, as we know by now is
the function is automatically called when a program is run. It may, in turn, call other functions. The main function should not call itself
recursively. The main()
function traditionally returns type int.
·
Scope Rules
o
Local Variables –
Local Variables are variables that are local to a particular function and are
not accessible outside of that function.
Two functions may have local variables with the same name, but they are
not the same variable and do not occupy the same memory space.
o
Procedural
Abstraction – Writing “black box” code.
We don’t need to know how a function does something; we only need to
know what the function actually does.
o
Global Constants
and Global Variables – Constants (initialized) and Variables (initialized or
non-initialized) declared outside the body of all function definitions are
global to all functions following the declaration within the given file.
§
Try to keep the
global constants grouped together for better readability.
§
Global Variables
– Try to avoid using as much as possible.
It makes code confusing and error-prone.
o
Blocks – A pair of curly braces is called a compound
statement. A compound statement that has
declarations is called a block. The
declarations are within the scope of the block.
#include <iostream>
using namespace std;
int main()
{
int num1 = 3, num2 = 4;
// Result below will be 3
cout <<"Num1 in main() before block = " << num1 << endl;
// Result below will be 4
cout <<"Num2 in main() before block = " << num2 << endl;
// Below is a new block within the curly braces
{
int num1 = 500, num3 = 50;
// Result below will be 500
cout <<"Num1 in block = " << num1 << endl;
// Result below will be 4
cout <<"Num2 in block = " << num2 << endl;
// Result below will be 50
cout <<"Num3 in block = " << num3 << endl;
}
// Result below will be 3
cout <<"Num1 in main() after block = " << num1 << endl;
// Result below will be 4
cout <<"Num2 in main() after block = " << num2 << endl;
return 0;
}
o
Nested Scopes –
Blocks within blocks have nested scopes.
The variables declared within a particular block are not within the
scope of the outer block(s).
o
Variables
Declared in a for
·
Parameters – C++
Allows you to make function calls with Call-by-Value and Call-by-Reference
Parameters
o
Call-by-Value
Parameters – We have been doing Call-by-Value thus far.
§
Call-by-Value
parameters are just like local variables that you declare within the body of a
function. The arguments of a function
call are copied into the formal parameters of the called function and are not
affected by changes made to the formal parameters within the called function.
o
Call-by-Reference
Parameters – Call-by-reference is accomplished by appending an
& to the type name.
§
Call-by-Reference
parameters are variables; not constants or expressions.
§
The arguments of
a function call are substituted for the formal parameters of the called
function and are affected by changes made within the called function.
§
Call-by-Reference
formal parameters are placeholders for the actual arguments used in a function
call.
// Consider the following
prototype
void getNumbers(int& num1, int& num2, int& num3);
// Consider the calling of
the function
int first, second, third;
getNumbers(first, second, third);
·
When the function
is called, the function is given the list of memory locations associated with
each name. Each memory location is
associated with the corresponding formal parameter.
first àAddress of first à num1
second àAddress of second à num2
third àAddress of third à num3
o
Constant
Reference Parameters – We will skip this for now, but be aware that this will
be helpful later on in the course when we discuss arrays and classes.
o
Mixed Parameter
Lists – We can mix Call-by-Value and Call-by-Reference parameters in the
function prototype and function definition.
//
The following prototype is legitimate.
void getNumbers(int& num1, int num2, int& num3, int& num4);
§ Be aware that num2 will be a local variable to getNumbers(). If you had meant to call-by-reference for all numbers, then num2 would be called an inadvertent local variable and the function would not work as intended.
|
Parameter |
How the Parameter Type is Used |
|
Call-by-Value |
Only the value of the argument is used. The formal parameter is a local variable that is initialized to the value corresponding to the argument. |
|
Call-by-Reference |
The argument is a variable. The argument variable is substituted for the formal parameter and any change made to the formal parameter is actually made to the argument variable |
·
Overloading and
Default Arguments
o
Overloading – C++
Allows multiple different definitions to the same function name. The function definitions must have different
numbers of formal parameters or some formal parameters of different types.
§
Cannot overload a
function name by giving two definitions that differ in the return type.
§
Cannot overload a
function name based solely on const
§
Cannot overload a
function name based solely on call-by value versus call-by-reference.
// Example by giving the declarations
void fun(int num1);
void fun(double num1);
void fun(int num1, int num2);
void fun(int num1, int num2, char answer);
// The function definitions would implemented according to the
// function declarations shown above. For example:
#include <iostream>
using namespace std;
void fun(int num1)
{
cout
<< “Called function taking one integer.\nnum1 = ” << num1 << endl;
}
void fun(int num1, int num2, char answer)
{
cout
<< “Called function taking
two ints and a char.”
<< “\nnum1 = ” <<
num1 << “\nnum2 = ” << num2
<< “\nanswer = ” << answer << endl;
}
int main()
{
fun(6);
fun(6, 15, ‘y’);
return 0;
}
§
The overloaded
function’s signature is the function’s name with the sequence of types in the
parameter list, not including the const keyword and not including the
ampersand. The overloaded functions each
have different signatures that the compiler uses for calling the correct function.
o
Rules for Resolving
Overloading
§
The first rule is
to look for an exact match. If the
number and types of arguments is an exact match to the formal parameters in the
function, then this rule is satisfied.
§
Type-Conversion
Match. The C++ compiler will do an
automatic type-conversion when needed.
If the first rule isn’t satisfied, then this rule is used.
·
Caution: It is best to program exclusively with the
first rule in mind. The following
example will cause a compiler error.
void f(int
n, double m);
void f(double n, int m);
f(4, 5); // Cannot determine which function to use
f(4., 5.);// Cannot determine
which function to use
o
Default Arguments
– Default values can be assigned to the formal parameters of overloaded
functions. Default values must be
ordered from right to left in the function declaration. Default values are not put in the function
definition.
// Example by giving the prototypes
void fun(int num1 = 100);
void fun(int num1, int num2, char answer = ‘n’);
#include <iostream>
using namespace std;
void fun(int num1)
{
cout
<< “Called function taking one integer.\nnum1 = ” << num1 << endl;
}
void fun(int num1, int num2, char answer)
{
cout
<< “Called function taking
two ints and a char.”
<< “\nnum1 = ” <<
num1 << “\nnum2 = ” << num2
<< “\nanswer = ” << answer << endl;
}
int main()
{
fun();
fun(6);
fun(10);
fun(10, 20);
fun(6, 15, ‘y’);
return 0;
}
//
The output is shown below.
Called function taking one integer.
num1 = 100
Called function taking one integer.
num1 = 6
Called function taking one integer.
num1 = 10
Called function taking two ints and a char.
num1 = 10
num2 = 20
answer = n
Called function taking two ints and a char.
num1 = 6
num2 = 15
answer = y
·
Testing and
Debugging Functions
o
The assert Macro
– Used like a function that takes a call-by-value parameter of type bool. It evaluates
the assertion argument. If it equates to
false, the program ends and an error message is issued; otherwise, nothing is
done and the program continues. Must use
the cassert header file to use the macro.
#include <iostream>
#include <cassert>
using namespace std;
int main()
{
int number;
for(int i = 0; i < 2; i++)
{
cout << "Enter a number that is not zero: ";
cin >>number;
assert(number != 0);
cout << "You entered " << number << endl;
}
return 0;
}
// The output is shown below
Enter a number that is not zero: 15
You entered 15
Enter a number that is not zero: 0
Assertion failed: number
!= 0, file c:\class\practice.cpp, line 263
o
Stubs and Drivers
– Stubs and Drivers are useful for building larger programs and testing
functions
§
Stub functions
are functions whose definition will be coded at a later time and used in a
larger program. They are coded to provide information to other functions during
the development of a larger, integrated program.
§
Driver functions
are functions that are specifically designed to test other functions. They are good for trying to “break” code in
the development process. Once a function
has been fully tested by a driver function, the likelihood that it will cause a
problem in the larger, integrated program is reduced.