User-Defined Functions - Special Topics

What is method overloading? How can we assign default values for parameters? What happens if I define a function after main()? What makes something global versus local?

In the previous lesson, we discussed how we can create our own functions and the components required. Now, we will review how to control the inputs and outputs of a function (such as through function name overloading and default values for parameters), as well as control what the function can ‘see’ (scope). Additionally, we’ll briefly talk about how to resolve a bug (a small error in your program) that happens when you define a function after main().

Function Name Overloading

Function name overloading, aka method overloading, is what happens when two or more functions have the same name (same capitalization, same spelling). Because they have the same name, we must distinguish which function we’re actually trying to call. Therefore, when overloading the functions’ name, remember that either of the following must be true:

  • each function that shares a name has a different number of parameters
  • each function that shares a name has different parameter data types
int addNumbers (int a, int b);
int addNumbers (int a, int b, int c);
double addNumbers (double a, double b);
double addNumbers (double a, int b);

The above code block is an example of function name overloading that will not cause an error. One thing to note, however: I have only declared, not defined, these addNumbers functions (more on this later).

Say I overload a functions’ name but state different return types, I will get the following error.

What happens if I just name my parameters differently? I get a redefinition error:

In summary, when overloading a function name, you need to distinguish which function you’re calling by either the parameter data type or number of parameters.

Declared Versus Defined

When you declare a function or a variable, you are telling the compiler that the function exists.

// a function declaration looks like this:
int functionA (int a, double b); // there is no body to this function

int main() {
	int c; // a variable is declared -- it has no given value
	return 0;
}

When you define a function, you give it a body to let the compiler know that the function contains a sequence of commands that should be executed.

// a function definition looks like this:
int functionA (int a, double b){
	cout << " 1st param = " << a << "; 2nd param = " << b << endl;
	return 0;
}

int main() {
	int c = 500; // a variable is initialized, aka defined
	return 0;
}

If you declare but not define a function when you make a call to it, you may get the following error at compile time:

Functions After main()

Sometimes, I might ‘accidentally’ define a function after main() and get this error:

My function was not declared in this scope and the compiler says that it encountered this error on line 6. When I look at line 6, I double-check how I call the function addNumbers, which seems correct.

However, the compiler reads code in the same manner we read a read: left to right, top to bottom. When we don’t understand a certain chunk of text, we might flip to previous pages in our book and re-read. We never (in theory, and should not) continue reading ahead in the hope that we’ll eventually ‘figure it out’.

A compiler is the same. Once it finds a function call, it checks the previous lines for the function’s definition. If it doesn’t find it, the compiler stops reading and says that the function was not declared in [my] scope.

To resolve this, we can say earlier on that the function addNumbers exists but its definition can be found later on in the document. We do this by stating the function’s prototype (aka declaring the function’s existence).

Default Values / Optional Parameters

Setting a default value for a function’s parameter makes the parameter optional. When you make a call to the function and choose to omit the optional parameter, your program will still compile successfully, as shown below.

int addNumbers(int a, int b, int c = 0){ // first two params are required, the third param is not
	return a + b + c;
}

int main(){
	cout << addNumbers(3, 7); 		// does 3 + 7 + 0, prints 10
	cout << addNumbers(2, 4, 8); 	// does 2 + 4 + 8, prints 14
}

You can also think of optional parameters as an abbreviation of function name overloading. The code above can also be written using name overloading:

int addNumbers(int a, int b, int c){ // all three parameters are required
	return a + b + c;
}

int addNumbers(int a, int b){ // all two parameters are required
	return a + b;
}

int main(){
	cout << addNumbers(3, 7); 		// calls the addNumbers function that takes 2 params
	cout << addNumbers(2, 4, 8); 	// calls the addNumbers function that takes 3 params
}

The default value for a parameter can be any value as long as it corresponds to the parameter’s data type. For example:

int assignSalary(string position, double hourlyWage = 11.50){
	double annualSalary;
	if (position == "Manager"){
		annualSalary = hourlyWage * 10; 
	} else {
		annualSalary = hourlyWage;
	}
	return annualSalary;
}

This function calculates an employee’s yearly salary by considering his/her position in the company and his/her hourly wage. By default, the hourly wage is $11.50. Having a default value in this context is good, especially for new employees whose hourly wage has not yet been determined.

Variable Scope

With this new understanding of functions comes great responsibility. Where your declare your variables and how you name them are now important. Your functions cannot ‘see’ what inside other functions. This also applies to your loops and conditionals. So, when you accidentally assume they can, you’ll encounter problems with variable scope.

Variables can be declared outside of a function, and therefore considered global (aka available to all functions in your program file). On the other hand, a variable declared inside a function is local (aka can only be seen by that function only). To demonstrate:

int a = 10; // is global

int main(){
	int b = 5; // is local
	return a + b; // 10 + 5 = 15, a & b are within the scope of main()
}

As previously mentioned, variable scope also applies to conditionals and loops (and anything else that encases with brackets { }).

In the image above, variable int b is declared and defined inside the first if-statement. Therefore, it is hidden inside that if-statement and cannot be seen by anything beyond or surrounding the if-statement. The errors when compiled say that the second if-statement as well as main() cannot access variable b.

When I comment out the problem lines (lines 13 to 15, and line 18), the console output shows what actually is within scope.

Different Scopes, Same Name

For whatever reason, you may choose to name a local and a global variable the same. When the function uses a variable, it follows these steps:

  1. The function will first look within it’s local scope.
  2. If the variable cannot be found within the local scope, then the function zooms out and searches the global scope.
  3. If the variable is not found in the global scope, the compiler terminates the program.
  4. The compiler thinks either the variable does not exist or the variable is not within the function’s scope.

The question becomes, “how can we distinguish between the two variables?” When we want to use the global variable, we put two colons in front of its name when called (::a).

Next Steps

Functions are extremely useful because they compartmentalize our program. Remember that functions should do just one job, and do it well (a reasonable output for all possible inputs). If a function does more than one job (such as performing adding and multiplication, as well as ask the user for inputs), it will be hard to reuse the function. What happens when you just want to multiply two numbers? Or if you just want to store the console inputs? As a (almost) real-world example, what issues do think we would have if our hearts were responsible for circulation as well as our breathing?

In this lesson, we demonstrated certain errors that might arise and how we can solve them. This was also our brief introduction to debugging, that is resolving bugs (aka errors) in our code.


© 2020. Some rights reserved.