 |
Exploring Functions
|
|
|
Functions and their Arguments |
|
|
When a function receives an argument, it performs one
of two actions with regards to the value of the argument; it might modify
the value itself or only use the argument to modify another argument or
another of its own variables. If you know that the function is not
supposed to alter the value of an argument, you should let the compiler
know. This is a safeguard that serves at least two purposes. First, the
compiler will make sure that the argument supplied stays intact; if the
function tries to modify the argument, the compiler would throw an error,
letting you know that an undesired operation took place. Second, this
speeds up execution.
To let the compiler know that the value of an argument must stay constant,
use the const keyword before the data type of the argument. For example,
if you declare a function like void Area(const string Side), the Area()
function cannot modify the value of the Side argument. Consider a function
that is supposed to calculate and return the perimeter of a rectangle if
it receives the length and the width from another function, namely main().
Here is a program that would satisfy the operation (notice the Perimeter()
function that takes two arguments): |
#include <iostream>
using namespace std;
float Perimeter(float l, float w)
{
double p;
p = 2 * (l * w);
return p;
}
int main()
{
float length, width;
cout << "Rectangle dimensions.\n";
cout << "Enter the length: ";
cin >> length;
cout << "Enter the width: ";
cin >> width;
cout << "\nThe perimeter of the rectangle is: "
<< Perimeter(length, width) << "\n\n";
return 0;
}
This would produce:
Rectangle dimensions.
Enter the length: 35.55
Enter the width: 28.75
The perimeter of the rectangle is: 2044.12
|
As you can see, the Perimeter() function does not
change the values of the length or the width. To reinforce the purpose of
the assignment, you should make this clear to the compiler. To make the
length and the width arguments constant, you would change the declaration
of the Perimeter() function as follows:
float Perimeter(const float l, const float w);
You can make just one or more arguments constants, and
there is no order on which arguments can be made constant. |
|
|
|
Practical Learning: Using Constant Arguments
|
|
- Create a new project in a directory of folder called Constant Arguments
- If necessary, create a source file. Save the source file as Main.cpp
- To apply the "constantness" of arguments passed to functions, change the
file as follows:
#include <iostream>
using namespace std;
// Rectangle
double MomentOfInertia(const double b, const double h)
{
return b * h * h * h / 3;
}
// Semi-Circle
double MomentOfInertia(const double R)
{
const double PI = 3.14159;
return R * R * R * R * PI/ 8;
}
// Triangle
double MomentOfInertia(const double b, const double h, int)
{
return b * h * h * h / 12;
}
int main()
{
double base = 7.74,
height = 14.38,
radius = 12.42;
cout << "Rectangle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << MomentOfInertia(base, height) << "mm\n\n";
cout << "Semi-Circle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << MomentOfInertia(radius) << "mm\n\n";
cout << "Triangle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << MomentOfInertia(base, height, 1) << "mm\n";
return 0;
}
|
- Test the program:
Rectangle
Moment of inertia with regard to the X axis: I = 7671.78mm
Semi-Circle
Moment of inertia with regard to the X axis: I = 9344.28mm
Triangle
Moment of inertia with regard to the X axis: I = 1917.95mm
|
- To use a mix of functions, change the program as follows:
#include <iostream>
using namespace std;
// Rectangle
double MomentOfInertia(const double b, const double h)
{
return b * h * h * h / 3;
}
// Semi-Circle
double MomentOfInertia(const double R)
{
const double PI = 3.14159;
return R * R * R * R * PI/ 8;
}
// Triangle
double MomentOfInertia(const double b, const double h, int)
{
return b * h * h * h / 12;
}
int main()
{
double length, height, radius;
double GetBase();
double GetHeight();
double GetRadius();
cout << "Enter the dimensions of the rectangle\n";
length = GetBase();
height = GetHeight();
cout << "Rectangle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << MomentOfInertia(Length, Height) << "mm\n\n";
cout << "Enter the radius of the semi-circle\n";
radius = GetRadius();
cout << "Semi-Circle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << MomentOfInertia(Radius) << "mm\n\n";
cout << "Enter the dimensions of the triangle\n";
length = GetBase();
height = GetHeight();
cout << "\nTriangle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << MomentOfInertia(Length, Height, 1) << "mm\n";
return 0;
}
double GetBase()
{
double b;
cout << "Enter Base: ";
cin >> b;
return b;
}
double GetHeight()
{
double h;
cout << "Enter Height: ";
cin >> h;
return h;
}
double GetRadius()
{
double r;
cout << "Enter Radius: ";
cin >> r;
return r;
}
|
- Test the program. Here is an example:
Enter the dimensions of the rectangle
Enter Base: 18.25
Enter Height: 14.15
Rectangle
Moment of inertia with regard to the X axis: I = 17235mm
Enter the radius of the semi-circle
Enter Radius: 15.55
Semi-Circle
Moment of inertia with regard to the X axis: I = 22960.5mm
Enter the dimensions of the triangle
Enter Base: 16.35
Enter Height: 12.75
Triangle
Moment of inertia with regard to the X axis: I = 2824.02mm
|
- After examining the program, return to your programming environment
|
An Argument as a Constant Reference |
|
|
If you pass an argument as reference, the compiler
would access the argument from its location. The called function can
modify the value of the argument. The advantage is that the code execution
is faster because the argument gives access to its address. The
disadvantage could be that if the calling function modifies the value of
the argument, when the function exits, the value of the argument would
have (permanently) changed and the original value would be lost (actually,
this can be an advantage as we have learned in the passed). If you do not
want the value of the passed argument to be modified, you should pass the
argument as a constant reference. When doing this, the compiler would
access the argument at its location (or address) but it would make sure
that the value of the argument stays intact.
To pass an argument as a constant reference, when declaring the function
and when implementing it, type the const keyword, followed by the argument
data type, followed by the ampersand operator, followed by a name for the
argument. When declaring the function, the name of the argument is
optional. Here is a function that receives an argument as a constant
reference: |
double CalculateNetPrice(const double& tax)
{
double original;
const double discount = 25;
Original = GetOriginalPrice();
double discountValue = original * discount / 100;
double taxValue = tax / 100;
double netPrice = original - discountValue + taxValue;
return NetPrice;
}
|
You can mix arguments passed by value, those passed as
reference, those passed by constant, and those passed by constant
references. You will decide, based on your intentions, to apply whatever
technique suits your scenario.
The following program illustrates the use of various techniques of passing
arguments: |
#include <iostream>
using namespace std;
// Passing an argument by reference
void GetOriginalPrice(double& originalPrice)
{
cout << "Enter the original price of the item: $";
cin >> originalPrice;
}
// Passing an argument as a constant reference
// Passing arguments by value
double CalculateNetPrice(const double& original, double tax, double discount)
{
discount = original * discount / 100;
tax = tax / 100;
double netPrice = original - discount + tax;
return netPrice;
}
int main()
{
double taxRate = 5.50; // = 5.50%
const double tiscount = 25;
double price;
double original;
void Receipt(const double& orig, const double& taxation,
const double& dis, const double& final);
GetOriginalPrice(original);
price = CalculateNetPrice(original, taxRate, discount);
Receipt(original, taxRate, discount, price);
cout << "\n\n";
return 0;
}
void Receipt(const double& original, const double& tax,
const double& discount, const double& finalPrice)
{
cout << "\nReceipt";
cout << "\nOriginal Price: $" << original;
cout << "\nTax Rate: " << tax << "%";
cout << "\nDiscount Rate: " << discount << "%";
cout << "\nFinal Price: $" << finalPrice;
}
|
Practical Learning: Passing Arguments by Constant References
|
|
- To illustrate the passing of arguments by reference and by constant
references, change the program as follows:
#include <iostream>
using namespace std;
// Rectangle
double MomentOfInertia(const double& b, const double& h)
{
return b * h * h * h / 3;
}
// Semi-Circle
double MomentOfInertia(const double& R)
{
const double PI = 3.14159;
return R * R * R * R * PI/ 8;
}
// Triangle
double MomentOfInertia(const double& b, const double& h, const int&)
{
return b * h * h * h / 12;
}
int main()
{
double length, height, radius;
void GetBaseAndHeight(double&, double&);
void GetRadius(double&);
cout << "Enter the dimensions of the rectangle\n";
GetBaseAndHeight(length, height);
cout << "Rectangle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << MomentOfInertia(length, height) << "mm\n\n";
cout << "Enter the radius of the semi-circle\n";
GetRadius(radius);
cout << "Semi-Circle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << MomentOfInertia(radius) << "mm\n\n";
cout << "Enter the dimensions of the triangle\n";
GetBaseAndHeight(length, height);
cout << "\nTriangle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << MomentOfInertia(length, height, 1) << "mm\n";
cout << "\n\n";
return 0;
}
// Passing arguments by reference
void GetBaseAndHeight(double& B, double& H)
{
cout << "Enter Base: ";
cin >> B;
cout << "Enter Height: ";
cin >> H;
}
void GetRadius(double& R)
{
cout << "Enter Radius: ";
cin >> R;
}
|
- Test the program. Here is an example:
Enter the dimensions of the rectangle
Enter Base: 18.85
Enter Height: 15.55
Rectangle
Moment of inertia with regard to the X axis: I = 23625.5mm
Enter the radius of the semi-circle
Enter Radius: 14.25
Semi-Circle
Moment of inertia with regard to the X axis: I = 16192.7mm
Enter the dimensions of the triangle
Enter Base: 8.95
Enter Height: 11.25
Triangle
Moment of inertia with regard to the X axis: I = 1061.94mm
|
- After testing the program, return to your programming environment
- To further mix the passing of arguments, change the program as follows:
#include <iostream>
using namespace std;
// Rectangle
// This function receives one argument by reference and two arguments
// by constant references
void MomentOfInertia(double& moment,
const double& b, const double& h)
{
moment = b * h * h * h / 3;
}
// Semi-Circle
// This function receives one argument by reference and one by
// constant reference
void MomentOfInertia(double& moment, const double& R)
{
const double PI = 3.14159;
moment = R * R * R * R * PI/ 8;
}
// Triangle
// This function receives one argument by reference, two arguments by
// constant references and one argument by value
void MomentOfInertia(double& moment,
const double& b, const double& h, const int&)
{
moment = b * h * h * h / 12;
}
int main()
{
double length, height, radius, mRectangle, mSemiCircle, mTriangle;
void GetBaseAndHeight(double&, double&);
void GetRadius(double&);
cout << "Enter the dimensions of the rectangle\n";
GetBaseAndHeight(length, height);
MomentOfInertia(mRectangle, length, height);
cout << "Rectangle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << mRectangle << "mm\n\n";
cout << "Enter the radius of the semi-circle\n";
GetRadius(radius);
MomentOfInertia(mSemiCircle, radius);
cout << "Semi-Circle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << mSemiCircle << "mm\n\n";
cout << "Enter the dimensions of the triangle\n";
GetBaseAndHeight(length, height);
MomentOfInertia(mRectangle, length, height, 1);
cout << "\nTriangle\n"
<< "Moment of inertia with regard to the X axis: ";
cout << "I = " << mRectangle << "mm\n";
cout << "\n\n";
return 0;
}
// Passing arguments by reference
void GetBaseAndHeight(double& b, double& h)
{
cout << "Enter Base: ";
cin >> b;
cout << "Enter Height: ";
cin >> h;
}
void GetRadius(double& R)
{
cout << "Enter Radius: ";
cin >> R;
}
|
- Test the program. Here is an example:
Enter the dimensions of the rectangle
Enter Base: 12.85
Enter Height: 8.85
Rectangle
Moment of inertia with regard to the X axis: I = 2969.01mm
Enter the radius of the semi-circle
Enter Radius: 5.55
Semi-Circle
Moment of inertia with regard to the X axis: I = 372.59mm
Enter the dimensions of the triangle
Enter Base: 10.75
Enter Height: 6.75
Triangle
Moment of inertia with regard to the X axis: I = 275.511mm
|
- Return to your programming environment
|
Functions Local Definition |
|
|
Like a variable, a function can be part of a
namespace. To declare a function in a namespace, provide its return type,
followed by a name, followed by the argument(s), if any, inside of
parentheses. Here is an example: |
namespace InterestAndDiscount
{
double Principal;
double Rate;
int Time;
double CalculateDiscount();
double CalculateInterest();
double CalculateMaturity();
}
|
A member function of a namespace can be accessed using
the scope access operator.
There are two main ways you can implement a member function. In the body
of the namespace, which is a local implementation, delimit the body of the
function with an opening curly bracket “{“ and a closing curly bracket
“}”. A function that is a member of a namespace has complete access to
the member variables of the same namespace. Therefore, you do not have to
pass the member variables as arguments to the member functions. Here is an
example: |
#include <iostream>
using namespace std;
namespace InterestAndDiscount
{
double Principal;
double Rate;
int Time;
double GetInterestRate()
{
return Rate / 100;
}
double CalculateInterest()
{
return Principal * GetInterestRate() * Time;
}
double CalculateMaturity()
{
return Principal + CalculateInterest();
}
}
int main()
{
using namespace InterestAndDiscount;
Principal = 12500; // $
Rate = 12.25; // %
Time = 4; // Years
cout << "Interest Calculation";
cout << "\nPrincipal: $" << Principal
<< "\nRate: " << Rate << "%"
<< "\nTime: " << Time << " years"
<< "\nInterest: $" << CalculateInterest()
<< "\nMaturity: $" << CalculateMaturity() << "\n\n";
return 0;
}
This would produce:
Interest Calculation
Principal: $12500
Rate: 12.25%
Time: 4 years
Interest: $6125
Maturity: $18625
|
If a nested namespace has its own functions, you can
also implement them in the body of the nested namespace. Here is an
example: |
namespace InterestAndDiscount
{
double Principal;
double Rate;
int Time;
double GetInterestRate()
{
return Rate / 100;
}
double CalculateInterest()
{
return Principal * GetInterestRate() * Time;
}
double CalculateMaturity()
{
return Principal + CalculateInterest();
}
namespace Discounter
{
double Maturity;
double DiscountRate;
double TermOfDiscount;
double Discount()
{
return Maturity * DiscountRate * TermOfDiscount;
}
}
}
|
After locally implementing the member functions of a
nested namespace, you can access its members and display their value in
the main() function as done above. |
|
Functions Global Definitions |
|
|
To implement a member function outside the body of a
namespace, provide the return type, followed by the name of the namespace,
followed by the scope access operator “::”. Here is an example:
|
namespace InterestAndDiscount
{
double Principal;
double Rate;
int Time;
double GetInterestRate();
double CalculateInterest();
double CalculateMaturity();
}
InterestAndDiscount::GetInterestRate()
{
return Rate / 100;
}
InterestAndDiscount::CalculateInterest()
{
return Principal * GetInterestRate() * Time;
}
InterestAndDiscount::CalculateMaturity()
{
return Principal + CalculateInterest();
}
|
To implement the member functions of a nested
namespace outside of the parent namespace, you must qualify each member
function to specify the function (or the variable) you are calling. Here
is an example: |
namespace InterestAndDiscount
{
double Principal;
double Rate;
int Time;
double GetInterestRate();
double CalculateInterest();
double CalculateMaturity()
namespace Discounter
{
double Maturity;
double DiscountRate;
double TermOfDiscount;
double Discount();
}
}
. . .
InterestAndDiscount::Discounter::Discount()
{
return Maturity * DiscountRate * TermOfDiscount;
}
|
Namespaces and External Functions |
|
|
The member variables of a namespace are variables like
any of those we have used so far. They can request their values from an
outside function. Here is an example: |
#include <iostream>
using namespace std;
namespace InterestAndDiscount
{
. . .
}
. . .
int main()
{
using namespace InterestAndDiscount;
double GetThePrincipal();
cout << "Loan Processing\n";
cout << "Enter the following values\n";
Principal = GetThePrincipal();
cout << "Rate (between 0 and 100): ";
cin >> Rate;
cout << "Time (number of years): ";
cin >> Time;
cout << "\nInterest on a loan";
cout << "\nPrincipal: $" << Principal;
cout << "\nRate: " << Rate << "%";
cout << "\nTime: " << Time << " years";
cout << "\nInterest: $" << CalcInterest();
cout << "\nMaturity Value: $" << CalcMaturityValue();
return 0;
}
double GetThePrincipal()
{
double P;
cout << "Principal: $";
cin >> P;
return P;
}
|
The member variable of a namespace can also be passed
as argument to a function. When passing the argument, if the using
namespace routine has been entered, you can pass the argument like any
other. Otherwise, you should qualify the namespace member with the ::
operator. In the following example, one member of a namespace is passed by
its name only because of the previous using namespace. The other members
are passed by being qualified, which is for demonstration purposes only: |
#include <iostream>
using namespace std;
namespace InterestAndDiscount
{
. . .
}
. . .
int main()
{
using namespace InterestAndDiscount;
void GetThePrincipal(double& p);
void RateAndTime(double &r, double &t);
cout << "Loan Processing";
cout << "\nEnter the following values\n";
GetThePrincipal(Principal);
RateAndTime(InterestAndDiscount::Rate, InterestAndDiscount::Time);
cout << setiosflags(ios::fixed) << setprecision(2);
cout << "\nInterest on a loan";
cout << "\nPrincipal: $" << Principal;
cout << "\nRate: " << Rate << "%";
cout << setiosflags(ios::fixed) << setprecision(0);
cout << "\nTime: " << Time << " years";
cout << setiosflags(ios::fixed) << setprecision(2);
cout << "\nInterest: $" << CalcInterest();
cout << "\nMaturity Value: $" << CalcMaturityValue();
return 0;
}
void GetThePrincipal(double& P)
{
cout << "Principal: $";
cin >> P;
while( P < 0 )
{
cout << "Enter a positive number: $";
cin >> P;
}
}
void RateAndTime(double &rate, double &time)
{
do {
cout << "Rate (between 0 and 100): ";
cin >> rate;
} while(rate < 0 || rate > 100);
do {
cout << "Time (Nbr of Years): ";
cin >> time;
} while(time <= 0 || time >= 30);
}
|
Although as a smart programmer you can create any
function to perform a desired job, the C++ language provides a series of
functions already made so you can just add them to your program without
caring how they work, all you need to know is what these functions do. The
functions that are part of the C++ language are highly valuable, were
tested sufficiently, and are completely reliable. The C++ built-in
functions are made for various assignments ranging from algebra, geometry,
trigonometry, and finance, etc. Besides the functions that are part of the
C++ Standard, each compiler ships with its own set of functions that may
not be available on other compilers. Borland C++ Builder provides an
extremely rich library of functions. |
|
Asserting a Value or an Expression |
|
|
Most of the values you use in your program will need
to fit in an interval of your choice. For example, when requesting the age
of a person, you would need such a value to be positive. After all, you do
not expect a person to be –26 years old. C++ provides a function that
can be used to check that a value or expression responds to a criteria of
your choice. This function is called assert() and is defined in the
cassert library of the std namespace. Its syntax is: |
void assert(int Expression);
|
The assert() function considers an expression as its
argument and tests it. This function is used in the same context as the
conditional statements we will study in the next lesson of this book. If
the Expression is true, assert() acknowledges that and lets the compiler
continue with the next operation. If the Expression is false, assert() displays a (nasty) message. Although we have not yet learned
conditional statements, the following is an example that requests the age
of a student and checks that the supplied age is valid only if the student
is older than 8: |
#include <iostream>
#include <cassert>
using namespace std;
int main()
{
float StudentAge;
cout << "Type Student's Age: ";
cin >> StudentAge;
assert(StudentAge > 8);
cout << "Student Age: " << StudentAge << "\n\n";
return 0;
}
|
The C++ language also provides a series of functions
to perform various mathematically related operations. The functions are
defined in various libraries and this can depend on the compiler you are
using.
The functions defined in the cmath library are: |
|
| acos |
cos |
fmod |
modf |
tan |
| asin |
cosh |
frexp |
pow |
tanh |
| atan |
exp |
ldexp |
sin |
|
| atan2 |
fabs |
log |
sinh |
|
| ceil |
floor |
log10 |
sqrt |
|
|
|
Additional functions are defined in the cstdlib
library and they are: |
|
| abs |
labs |
srand |
| div |
ldiv |
rand |
|