In this video, we'll learn about function overloading.
In c++, we can have functions with different parameter
lists that have the same name.
For example, I may have many ways to display information to the screen
depending on what I want to display.
So rather than having many functions with different names
such as display char, display int, display double and so forth, we
can have a single name display, and then implement a version of the
function for each type of parameter.
Then we just let the compiler figure out which function to use based
on the function call arguments and the defined function parameters.
It sounds more complicated than it is, and we'll see an example
in the next slide that'll show you just how simple it is.
This is a great use of abstraction since as a developer all I need
to think is display and pass in whatever information I need.
I don't have to keep track of dozens of different function names.
In software engineering, we have a principle called polymorphism, which
means many forms for the same concept.
This is an example of polymorphism.
Let's see what overloaded functions look like.
In this example, we have two functions that are both called add numbers.
The first function expects two integers and the second
function expects two doubles.
I'm only showing the function prototypes in this slide,
but we actually have to implement both functions.
I'll do that in the next slide.
Notice that in main I'm calling both functions, but I don't have
to think about different names.
I just want to add two numbers.
If I call add numbers with two integers, then the int int
version of the function is called.
If I call add numbers with two doubles, then the double double
version of the function is called.
In this slide, I'm providing the function definitions for the
add numbers functions that we prototyped in the previous slide.
It's important to understand that we must implement all
of the overloaded versions.
Notice that the code for these two functions is nearly
identical except for the types.
C++ has a feature called function templates that allow us to just
write one generic version of the add numbers function and it
will take care of providing the correct version when called.
Function templates are a little bit more advanced topic, and we'll
discuss it when we talk about the standard template library.
Let's take a look at another example.
In this example, I'll only show the function prototypes.
But remember, that you'd have to implement all of these.
I have five functions named display.
The parameter list for these functions must be different so
the compiler can tell them apart.
Once these functions are implemented, I can call
display and pass in my data.
The compiler will check the type of the argument in the function and
match it to one of the available overloaded display functions.
If it can't match it or if it can't convert the argument to one that
matches, then we get a compiler error.
There is one restriction to function overloading.
The return type is not considered when the compiler is trying to
determine, which function to call.
So here we have two overloaded functions, both called get value,
and both expect no parameters.
The only difference is that one returns an integer
and the other a double.
This will produce a compiler error since the only
difference is the return type.
Consider the output statement at the bottom of the slide.
Which function would the compiler call?
It could call either and that's the problem, it won't guess.
So it'll produce a compiler error.
Overloading functions is used extensively in object-oriented design,
and we'll see it again when we design our own c++ object-oriented classes.
I'm in the CodeLite IDE in the section 11 workspace.
And I'm in the function overloading project.
Here, I just want to go through some examples of overloading a function.
Remember, overloading a function simply means using the same name for
various forms of that same function.
In this case, it's print.
And what I want to do is, I want to overload print to work with
different types, so you can see some of the function prototypes here.
In this case, I've got an int version of print so if I pass in an integer
to print, it'll use this version of the function, one that expects
a double, one that expects a c++ string object, one that expects two
c++ string objects and the last one expects a vector of string objects.
Okay, remember, we have to implement each one of these
and that's what I'm doing here.
I'm just saying printing in printing double, so that we
know which one is being called.
So in the case of print int, it simply says printing integer
and it displays the number.
For double, it's doing the same thing string and so forth.
Okay, and then for the vector version, it's just iterating
through the vector and printing out all the strings in the vector.
So let's see how this works?
In this first case, I'm simply calling print and I'm passing in 100.
Now 100 is an integer.
So I expect that this integer version of print is the one that's going to
be called and it'll say printing it.
Okay, so let's give that a try.
Printing in 100, exactly what we expected.
In this version, I'm passing in a character.
Now characters are always promoted to integers.
So in this case, it's going to call the same function the one that
expects an integer, but it's not going to print out the character a.
It's going to print out 65, which is the ASCII code for a.
Remember, it's using integers here.
So let's run that, and there you see it says printing
integer and it's printing to 65.
In this case, I'll comment both of these out.
In this case, we're going to use the double version which is this
version right up here print expecting a double and it's just going to
say printing double and display the number that was passed in.
So in the first example, I'm passing in 123.5.
No problem, 123.5 is a double.
So that should match perfectly.
However, in the second example, that f right there makes
this a float, not a double.
So in this case, I'm passing a float to the function.
But there is no overloaded function that expects a float.
So what the compiler does is it will promote the float to a double, it'll
match that function and call it.
So both these statements will use that same function.
Okay, let's run that.
And you can see here 123.5.
And in this case 123.3.
Okay, so what about c-style strings.
Let me uncomment that out.
And remember, we've got a version of print right here that expects
a string object, and it simply prints out that string object.
However, I'm not passing in a string object here, I'm passing
in a c-style string, an array of characters, that does not
match that function prototype.
But the c++ compiler knows how to convert a c-style string,
right, an array of characters to a c++ string object.
So it will use that conversion.
And it'll pass in the literal, make it an object and then we're
going to display the object.
So if we run it, you'll see a printing string, c-style string.
Okay, exactly what we expect.
Once you understand how the conversions work, it
all makes a lot of sense.
In this next example, I'm declaring right here s to be a string object.
This is not a c-style string, this is a c++ style string object.
And I'm passing it into print.
That's going to match exactly and it'll print out c++ string.
All right, in this print statement here, we're passing
in a c-style string and a string object, okay, both.
Now we've got a version of us of a print function right here
that expects two string objects.
Well, no problem, right.
We'll convert the first argument to a string object, and then
pass the second one as well.
So in this case, when I run it you'll see that that same printing
two strings method is called with a c-style string and a c++ string.
Okay, pretty cool.
And then the last one is this vector.
Here I'm creating a vector called three stooges, which
is a vector of string objects.
And we've got Larry Moe and Curly in there.
And I'm calling the print function and passing in three stooges.
So it's going to match this one right here that expects a vector of strings.
And I'm just going to iterate through there and print
out the three stooges name.
So the three strings in that particular vector.
Okay, so I'll run it, and there you go printing vector of
strings: Larry Moue and Curly.
So you can see how nice it is to be able to overload functions.
The nice part about it isn't so much all the code that I've
written, that's not that great.
The nice part about it is once the code is written,
I can just think print.
So if I'm solving an application, I'm printing print print print.
I'm printing everywhere.
I'm not doing -- I don't have to do print int and
print string and so forth.
I'm just thinking print, which is what abstraction is all about.
Now there's some -- couple of things you need to worry about
when you're overloading functions.
Be careful when you overload functions and you use default arguments.
Okay, I'll show you why.
Here's an example of the print with the integer version.
I could certainly default this to a 100, let's say.
So if I don't supply the argument to the print function,
it's going to default to 100.
And let's test that out, we'll do it right up here.
Okay, so at this point now, we should get 100 here, and then 100
on the next line as well, right.
So let's run that, and you can see there's the 100.
That's the default argument right there.
That makes sense.
But what about if we didn't do that.
And we did it down here in the print double.
Let's say that we want to default this one to 125.5.
Now when I call print here, it will default to 125.5 because
it's up here right there.
That's the one that's going to match, right.
So there's the 125.5, and the double version gets called.
Again, it makes sense.
Where you run into trouble is when you do both.
Let's say you do something like that.
Now you're really in trouble because the compiler can't
figure out, which one to use.
Again, look at the print call right here.
It says print but it doesn't supply any type information.
So now the compiler can choose either one because they both match.
And it's not going to choose either, it's going to
give you a compiler error.
And when I write it, it'll say something about an
ambiguous call right here.
Error call of overloaded, print is ambiguous.
So whenever you use overloaded functions with default arguments,
you've got to be a little careful.
So let me get rid of that.
And I'll set those back up to the way they were.
Okay, so that covers overloaded functions.
We're going to see overloaded functions a lot when we talk
about object orientation and when we create our own classes.
We want to have different ways to initialize our classes.
You saw that with the vector class and the string class, all the different
ways that we can create those objects.
Well, that's all based on overloading functions.