C++ enables several functions of the same name to be defined, as long as they have different signatures. This is called function overloading. The C++ compiler selects the proper function to call by examining the number, types and order of the arguments in the call.
Function overloading is used to create several functions of the same name that perform similar tasks, but on different data types. For example, many functions in the math library are overloaded for different numeric types the C++ standard requires float, double and
long double overloaded versions of the math library functions.
- Procedural Overloading of Function:
Figure 3-6 uses overloaded square functions to calculate the square of an int and the square of a double.
C++ treats whole number literal values as type int.
Similarly, line 24 invokes the double version of function square by passing the literal value
7.5, which C++ treats as a double value. In each case the compiler chooses the proper
function to call, based on the type of the argument. The last two lines of the output window
confirm that the proper function was called in each case.
Figure 3-6
#include <iostream>
using namespace std;
// function square for int values
int square(int x ){
cout << "square of integer " << x << " is ";
return x * x;
}
// function square for double values
double square(double y ){
cout << "square of double " << y << " is ";
return y * y;
}
// ----- main
int main(){
cout << ; // calls int version
cout << endl;
cout << ; // calls double version
cout << endl;
}
Output of program below:
square of integer 7 is 49
square of double 7.5 is 56.25
Member functions within the same
class
can be overloaded. Remember that a function is called based on its
signature,
which is the list of argument types in its parameter list. Consider adding to the class
ch_stack
a
pop
operation which has an integer parameter that is the number of times the
ch_stack
should be popped. It could be added as the following function prototype within the
class
:
class ch_stack {
.....
char pop(int n); //within ch_stack
.....
};
char ch_stack::pop(int n)
{
assert(n <= top);
while(n-- > 1)
top--;
return s[top--];
}
The definition that is invoked depends on the actual arguments to
pop
:
data.pop(); //invokes standard pop
data.pop(5); //invokes iterated pop
A member function is conceptually part of the type. There is no distinct member function
pop
for each
ch_stack object.
The declaration
ch_stack s, t, u;
creates three separate
ch_stack
objects of
sizeof(ch_stack)
bytes. Each of these objects has its own data members:
char s[max_len];
int top;
A member function is conceptually part of the type.
There is no distinct member function for any of these three
ch_stack
objects.
NOTE: To follow the const-correctness principle, it is always a good idea to declare member functions that do not change any data member of the object as being const. These
member functions are also called
inspectors, compared to
mutators for non-const member functions.
This section discusses the implementations for the Employee member functions. The Employee constructor sets the initial values for the Employee's
data members. By default, new employees have no name, an employee number of -1, the default starting salary, and a status of not hired.
This constructor implementation shows a second mechanism to initialize class member variables. You can either put the initialization between the curly braces in the body of the constructor, or you can use a
constructor initializer, which follows a colon after the constructor name.
#include <iostream>
#include "Employee.h"
using namespace std;
namespace Records {
Employee::Employee()
: mFirstName("")
, mLastName("")
, mEmployeeNumber(-1)
, mSalary(kDefaultStartingSalary)
, mHired(false)
{
}
The promote() and demote() methods simply call the setSalary() method with a new value. Note that the default values for the integer parameters do not appear in the source file; they are only
allowed in a function declaration, not in a definition.
void Employee::promote(int raiseAmount){
setSalary(getSalary() + raiseAmount);
}
void Employee::demote(int demeritAmount){
setSalary(getSalary() - demeritAmount);
}
The hire() and fire() methods just set the mHired data member appropriately.
void Employee::hire(){
mHired = true;
}
void Employee::fire(){
mHired = false;
}
The display() method uses the console output stream to display information about the current employee. Because this code is part of the Employee class, it could access data members, such as mSalary, directly instead of using getSalary().
However, it is considered good style to make use of getters and setters when they exist, even within the class.
void Employee::display() const
{
cout << "Employee: " << getLastName() << ", " << getFirstName() << endl;
cout << "-------------------------" << endl;
cout << (mHired ? "Current Employee" : "Former Employee") << endl;
cout << "Employee Number: " << getEmployeeNumber() << endl;
cout << "Salary: $" << getSalary() << endl;
cout << endl;
}