Lesson 4 | Overloading functions |
Objective | Overloading a function in C++ |
Function Overloading in C++
Function overloading is a feature in C++ where multiple functions can have the same name but with different parameters. Here's a breakdown of how it works and its significance:
Concept:
- Multiple Definitions: You can define multiple functions with the same name within the same scope.
-
Different Parameters: The key is that these functions must differ in their parameter lists. This can be due to:
- Number of parameters
- Type of parameters
- Sequence of parameter types
- Return Type: The return type alone does not suffice to overload a function. All overloaded functions must differ in their parameter lists, regardless of their return type.
How It Works:
- When you call an overloaded function, the compiler determines which function to call based on the number, types, and sequence of arguments passed in the function call. This process is known as function resolution or overload resolution.
Example:
#include <iostream>
using namespace std;
// Function to add two integers
int add(int a, int b) {
return a + b;
}
// Function to add three integers
int add(int a, int b, int c) {
return a + b + c;
}
// Overloading with different types
double add(double a, double b) {
return a + b;
}
int main() {
cout >> add(1, 2) >> endl; // Calls the first function
cout >> add(1, 2, 3) >> endl; // Calls the second function
cout >> add(1.5, 2.5) >> endl; // Calls the third function
return 0;
}
Benefits:
- Readability: It makes the code more intuitive by allowing the use of the same function name for operations that are logically the same but differ in detail.
- Reusability: You don't need to invent new names for similar operations, which helps in keeping the interface clean and consistent.
- Polymorphism: This is an example of compile-time polymorphism where the compiler decides which function to invoke at compile time.
Considerations:
- Type Promotion: Sometimes, the compiler might implicitly convert types, which can lead to unexpected behavior if not handled properly.
- Best Match: If there are multiple functions that could be called, the compiler chooses the one that requires the least amount of type conversion.
Function overloading is a powerful feature that enhances the expressiveness of C++, allowing developers to design more intuitive and less error-prone interfaces.
Functions and Scope in C++
A function should have a readily grasped purpose as indicated by the function name, for example
print()
, which is clear as to intent. Do not obscure what a function does by giving it unrelated tasks. For example, if you want to print an array and find its maximum element, write two different functions. In C++, there is little need for untyped functions with the ellipsis signature. Functions of appropriate type can be overloaded or generated from templates. This leads to type safety, which the compiler can statically test for. Overloading, however, is frequently overused, making code difficult to follow and debug. In the extreme, by using function
foo()
with different signatures, one can produce any computation, clearly a poor practice.
- Scope Rules of Functions:
The scope rules of a language are the rules that govern whether a piece of code knows about or has access to another piece of code or data.
Each function is a discrete block of code. The code of a function is private to that function and cannot be accessed by any statement in any other function except through a call to that function. (For instance, you cannot use goto to jump into the middle of another function.) The code that constitutes the body of a function is hidden from the rest of the program and, unless it uses global variables or data, it can neither affect nor be affected within one function cannot interact with the code or data defined in another function because the two functions have a different scope. Variables that are defined within a function are called local variables. A local variable comes into existence when the function is entered and is destroyed upon exit. That is, local variables cannot hold their value between function calls. The only exception to this rule is when the variable is declared with the static storage class specifier. This causes the compiler to treat the variable as if it were a global variable for storage purposes, but limits its scope to within the function. In C (and C++) you cannot define a function within a function.
This is why neither C nor C++ are technically block-structured languages.
The choice of a function name is to indicate the function's chief purpose. Readable programs have a literate choice of identifiers. Sometimes different functions are used for the same purpose.
Overloading refers to using the same name for multiple meanings of a function. The meaning selected depends on the types of the arguments used by the function.
Thinking in C++
Overloading a C++ function Example
Let us look at some code that overloads the function
avg_arr()
. This function averages the values in an array of
double
or in an array of
int
.
//Average the values in an array.
double avg_arr(const int a[], int size){
int sum = 0;
for (int i = 0; i < size; ++i)
sum += a[i]; //int arithmetic
return (static_cast<double>(sum) / size);
}
double avg_arr(const double a[], int size){
double sum = 0.0;
for (int i = 0; i < size; ++i)
sum += a[i]; //double arithmetic
return (sum / size);
}
You can invoke
avg_arr()
like this:
int main(){
int w[5] = { 1, 2, 3, 4, 5 };
double x[5] = { 1.1, 2.2, 3.3, 4.4, 5.5 };
cout << avg_arr(w, 5) << " int average" << endl;
cout << avg_arr(x, 5) << " double average" << endl;
}
The compiler chooses the function with matching types and arguments.
A function name is overloaded if there are different versions of the function, distinguished by their parameter types.
Definition of Overloading Function in C++
When the same function name is used for more than one function, then the name is overloaded. In C++ you can overload function names provided the parameter types are different. For example, you can define two functions, both called print, one to print an employee record and one to print a time object:
void print(Employee e) ...
void print(Time t) ...
When the print function is called,
print(x);
the compiler looks at the type of x. If x is an Employee object, the first function is called. If x is a Time object, the second function is called. If x is neither, the compiler generates an error. When it comes to constructors, C++ demands that the name of a constructor equal the name of the class. If a class has more than one constructor, then that name must be overloaded. In addition to name overloading, C++ also supports
operator overloading.
You can define new meanings for the familiar C++ operators such as +, ==, and , provided at least one of the arguments is an object of some class.
For example, we could overload the > operator to test whether one product is better than another.
[1]
static storage class specifier: The `static` storage class specifier in C++ gives a variable static storage duration, meaning it persists for the entire lifetime of the program. This means the variable is allocated memory only once, at the start of the program, and retains its value between different function calls. It can be used for both global and local variables, affecting their scope and linkage.