Okay, next topic. When you get back to some core C++ ideas. So, we've outlined the methods you need for the homework. You can either use Prim or Kruskul or do both, compare them if you have energy and time. You're gonna add that to your pre-existing homework where you have already done graph algorithms. Try and refine whatever you learn from before, maybe your graph structure will be more elegant and now that you've learned more C++ you can rebuild even your basic graph class. But in any case, the new problem is to do a Kruskal or a Prim minimum spanning tree. And now we wanna introduce further ideas from C++. The critical ideas we want is a very neat idea. Something that hasn't been put into Java, for example. There are arguments over whether it's a good idea. But in lots of arenas, it's a very valuable idea. And that's called operator overloading. And that's where we provide new meanings to existing C++ operators. And, we've already used this as very rich idea in the IO community and the IO package where we have operator overloading of the left bit shift for doing output. And we're gonna show how important yet another idea is that we haven't seen yet in C++, which is using friend functions, especially when we wanna provide operator overloading. Okay, high level principle. User defined types should be indistinguishable from native types. Operator overloading is a mechanism that allows us, and conversion. Those are mechanisms that allow us to implement this principle. What do I mean by that? In the native language, if I have two ints, we all know what a+b means. But let's say I decide to add from a library or whatever a rational number. For those of you that don't remember your highschool math, a rational is just a number represented by two integers, q divided by b, so two-thirds is irrational, three-quarters is irrational, seven-elevens is irrational. Pi is not a rational, remember that the notion the other day of pi being transcendental. It can't be represented precisely by, there are ways to represent pi. There's a nearby rational, for example 7/22 is a fairly good approximation to pi when you have two small integers. And you can approximate pi as closely as you want by a rational, but you can't make it exact. Anyway, if I all of a sudden had some kind of package for doing rational arithmetic, I might also want to be able to have c + d. Because now mathematicians can add rationals. Again, if you remember your grade school work, they probably told you how to do this kind of addition. So your rational overloading of the plus operator would lead you to provide the algorithm for doing rational arithmetic. But, rationals can be used much like integers and doubles. So you might also expect that you should be able to do a + c. Where a was a normal int and c was a rational. And have a resulting conversion and convert the a to a rational. So now your rationals, this new thing, this new class that you've invented, G gets used as conveniently as the native type. That's part of the OOP principle. When you add a type, you want to add a type in a way that is consistent with pre-existing types in the C++ world. So we're gonna add a type, we're gonna add the type point. Points are very simple to type. Here's a point, we're going to think of a point as a pair in a two dimensional world, X and Y. Maybe that's the point. 1,1. As with good OO, we're gonna hide representation. So the actual underlying representation will be a pair of doubles. So we can also have the point 0.5, 0.5, really we should be thinking of that as 1.0, 1.0. So we're gonna use doubles. We're gonna have a bunch of constructors. Here's the constructor. That's the constructor of one argument, which basically takes an argument, makes it the x value, and leaves the y value as zero. So a point here is going to, if I wanna have a point whose value is initialized with three, I'm gonna end up with the point 3,0 through this constructor. This is gonna be important because this is also what we call a conversion constructor. So here's point s, we can think of it as having value zero-zero if we define that class properly with a default. And now we have a double, 3.5. And we say assign to s, d. Now d is a double, and we don't have a logic for assigning a double. So there needs to be a conversion. So d needs to be converted to the point 3.5,0. Once it's converted, then we can have the. Now, if we wanted to make that clearer, we could do it via an explicit assignment. But actually, this works as well. Why does it work? Because C++ allows for implicit conversion. In fact, I'm gonna go back to the previous slide because I'm gonna show you something that you can keep in mind. If you didn't want that behavior you would have to add the keyword. Explicit. An explicit in a way turns off Implicit conversion opportunity. So, there's lots of times that you don't really want that to happen because adding conversion opportunities in this language it's very sensitive because conversions allow signatures to be matched. They allow expressions to be evaluated, and you don't want that to happen inadvertently. Now, what about converting a point to double? Well, we know how to convert double to point. The way we convert double to point, is we write a conversion instructive. So, the argument, the double argument becomes an argument of value to instantiate some of the underlying data in point. But we can't write a conversion constructor in the native class double, double is a native type. You could think of it as having its own code somewhere in the world, it's really just built into the compiler. But there isn't a way to inform the compiler that I wanna change the rules about double. So, because there's no access to that code. So, C++ does provide an alternative, and we see the alternative here. You can, inside the class point, so again, we see that we have scoped resolution to point by providing operator double. The type to be converted to. We allow a point to be converted to a double. Now, whether that makes sense, whether that's a good idea, it probably only should be used where it's natural to the semantics of that type. What I mean by natural to the semantics of that type? Which somebody who is used to a draftsman, who is used to using two dimensional objects, two dimensional coordinates points. If you told him that I wanted to convert a point to a double, would he know what you meant? He doesn't know what you mean. If he doesn't know that you intend to use the distance, the vector is from the origin, which is basically what we're doing here, then it's a bad idea. Then you should probably provide something that's explicit, like the length of a point from the origin. You should have a name type. So, these conversion opportunities can easily lead to obscure programming. And they frequently should be very heavily justified and the justification should come from the domain, in which the type is used. But it's available, so now you can have what might be called a narrowing conversion. And you can turn a point into a double, or you could turn a point similarly into anything else. By providing it with whatever this type name is, a type and some semantics to convert to that type. And then you have conversion opportunities. Pretty powerful stuff. Oh, we've already talked a little bit about that. I've already pointed this out. Again, C++ 11, some of the earlier compilers already implemented, this allowed an explicit keyword. Once people started doings rampant operator overloading and conversion was soon discovered that things were happening subtly that nobody expected. And so, the explicit key work got introduced so that could be turned off, that opportunity could be turned off. Now, remember that in C++, one of the big changes from C is you have function overloading. So in C, if we wanted the sum of an integer array, we have call it sum if int array. If we wanted the sum of a double array, we have to give it a different name. We have to distinguish the name, even when the code read the same. C++ provides a number of ways to keep the name for something that where we're doing conceptually the same work using the same code. The two principle methods are ordinary overloading of functions. So, we can call both C adding of arrays of ints, and the adding of arrays of doubles, or even the adding of arrays of rational sum. And then they get discriminated by their signature. I maybe just write it, so if I want it, I could say, double, sum, double. Int, sum Int. The code would probably look the same. I could even do this with templates. And the compiler has to have a way, among the meanings available to it to choose which meaning. Otherwise, it becomes ambiguous, so you can, as a compiler error message, get the fact that the compiler is confused. Now, the compiler has a very complex and detailed way it as preferred selection. So exact match, exact match is always preferred if would, is passed in a set of arguments. And it's looks a third type, and it says, oh, I have exactly code for those types, then it picks that. No problem, everybody understands it. But now, we have all these promotions available. So again, just like with square root, remember square root is in the standard library, in the math library. Square root will work on an integer, even though it's intended to work on doubles. Why? Because the integer will be promoted automatically silently to a double. So again, what happens is, an overloading standard promotions, which are the safest kinds of promotions, are preferred over anything else. And so, if there's a signature and it requires a standard promotion, and there's no exact match, then this would be tried. The next in line is standard type conversions, including demotions. Then there are user defined conversions, much like those, the conversions we defined on point. And then there's a match to see if there's an ellipsis signature, and that's the worst. Now, if you're following any of this in my text, there's description of it. Page 198 to 200 with a detailed example using rational numbers, but it can be very complicated. And you can see where you have all these additional user defined conversion opportunities that you have to be very, very careful. Hence, the introduction of explicit to avoid silently ending up with execution of code that was uninvented. Friends, next big topic, we have these things called friends functions. There is a couple of friends, they're my own cats they're named Googly and Hamster. They're friends, though they're more frienemies than friends. They, cats being, even domesticated cats seem to be fairly territorial. So, they do hiss at each other, and they especially fight over their food bowls. But a critical thing about friends, and that's going to be true in C++ is, if you're a really good friend of mine, I'm gonna let you look at my private stuff. I'm gonna let you look at the private implementation of my class, that gives me the opportunity to act on your class, almost as if I was a member function. So a friend function has access as if it was a member function, but it's not a member function. If it's not a member function that means it has no access to what? Think about that. Is one thing that member functions always get, ordinary member functions always get, that functions that are called with arguments on the class don't get, and that's the disc pointer. Remember, a member function is activated through the disc pointer. Friend function get's to treat the private parts as if it was a member function, but doesn't get to directly access because that's not pass in an argument through the dot notation. There's a class point, and again, because the C object oriented idiom, we want the behavior of the bit shift operator to be used for IO. Again, here we see the IO signature that's our idiom. We need this call by reference, we pass back out call by reference. We have a non-mutated object passed by reference. And it's semantics are that we're gonna prevent a point looking like this. So, what we would see as output for a point that had hidden representation 1.0, 2.5 would be (1.0, 2.5), but this is going to fail. So, if we were in regular code and saying, C out, blah, blah, and the point name was q. We would get a compiler error, and the compiler error would say wouldn't have access to x and y. Why? X and y are private. This is an ordinary method. It does not have the ability to access hidden representation, can't make use of this. Well, oo says, that this must remain restricted, data hiding principle. So, the way around this, well, one idea you could try is to make this a member function. But, recall a member function has a default first argument. A default first argument has to be an object of type point. But for this to work in the syntax that's expected, the first argument has to be an ostream. It has to be in most an ostream, like C out. So, we can't make it a member function. A member function would solve the problem of getting access, but it wouldn't solve the problem of having the wrong argument order. And C++, courtesy of invented a way around to this kind of access problem, and that was to create friend functions. There is were friend becomes important. I use this special keyword friend that informs the compiler that it's got these privileges. It breaks the normal data hiding, so it should be used very carefully. Friend should be used very carefully. We allow it to have these important access privileges, and unlike a class method it does not have the dot argument. So, now we're going to pass in both arguments. We're gonna pass in an ostream and a point. And that'll be passed in through the argument list, which is another thing to consider. Because when we pass stuff in through the argument list, they're subject to conversion opportunities. So that's another point, very important and subtle point, that deals with the use of friends when we overload operators. Friends overload operators by having the arguments always go through the parameter list. A member functions overload the arguments by having your first argument be the class argument, namely at this point your argument, that is not overloaded to have conversion opportunity. So only a second, or other argument would have a conversion opportunity that leads to a different behavior, even when we can overload the same operator. And have the choice between friend overloading, member function overloading, we frequently choose friend overloading. I'm gonna make that point a couple of times cuz it's very subtle and important point. We do that to maintain symmetry. A friend overloading lets us use the argument list, the argument list means for binary operators, we get all the conversion opportunities symmetrically on both arguments. Here is a member function overloading of the operator +. So, think of what you're doing. You have two points, let's say we have (2, 1) and (1/2, 0). And we want, maybe that's an a, and that's a b. And we wanna be able to because that's what we are used to as mathematicians, be able to write naturally a + b. And we can see what we want that to be, that result. Let's call it c, we want c to come out (2 1/2, 1). And the way we do that is If we're overloading this with the notion that we have a plus b, we really are calling through to this point of a, and through the argument was b. That's really how this is getting activated. So, that's why x and y are without. Those fields are discriminable. See, these are really, this points at x, plus p of x. And that leads to two and a half, and similarly for the y, okay, if you understand everything I've just said, you're really getting all this. If you don't understand what I've just said, you gotta work with this. You gotta do your own stuff. You gotta overload operators. And see how that works. So this is very critical that what I just said, is clear to you. So here you've seen how to overload the binary plus using a member function, and here i'm just repeating what I said on the other with a different example, I have three points, I'm adding to a, b plus c, a is gonna end up being two and a half, seven. But, if instead that operator got overloaded in a way that we use the friend function instead of a member function, its invocation, Would instead call a friend function. And both b and c would be passed instead of this pointer. And an argument. This would be arg 1 and arg 2, and they would be completely symmetric. So in the completely symmetric case, all of the available conversion opportunities. So for example, remember, we can convert an ordinary double to a point. So that would mean, if you're following me, that if d was a double, and I did d + b This wouldn't work. Or member overloading. And that gives you the reason why you want it as a friend function. Friends are really important. They're really important especially when you're doing operator overloading. They're important in other considerations too, when you have two very strongly linked classes. Gotta warn you about operator overloading, people get infatuated with it, why? Cause it's a way to have a secret calculus. Everybody loves making their own secret notation. That's okay. If you're just playing with the stuff, and you're not in a company, and you're not using it professionally, where other people will need to interact with your code, do operating overloading to your heart's content. Maybe you can even come up with something close to APL if you remember this hugely powerful early IBM language, a man named Iverson, invented the language in which there was super powerful overloaded operators in effect. That worked very well with linear algebra calculations. And from what I recall, I may be wrong about it, somebody can write into a forum on this, I believe you could write out a linear programming algorithm using the Iverson notation. In relatively few lines of code, whereas if you had to use the language of the day such as Four Train and PL1, it would be a considerable amount of code. So you would have this enormously dense, powerful operator language, and this could be replicated. With operator overloading, but using this, one expects to conform to the rules of math. You generally are going to use it in domains for which people know what the meaning is. You don't want to have to explain that plus is something that it isn't. What does times mean in a domain where you are talking about clocks? Let's say you decide to design a package called a clock, and clocks have minutes, seconds, and hours. Well it's pretty easy to understand how, you know, I have an appointment a half an hour from now. So I want to know what time that's going to be. But now what's the times operator gonna be? Can I multiply two clock times, is that reasonable? So, you gotta be very careful, and you need to be comprehensive. So you're not going to go and overload + in a normal domain, in which- is understood as being the inverse operation. So normally you're gonna overload the complete algebra. You have to do what's expected. You have to do what's natural. And you have to do what fits in with the native C++ types. Okay, here's a quiz to this section. Not all operators are overloadable. Most operators in C++ are overloadable. Some operators like scope resolution remember that's colon colon. That's not overloadable. Why? That would cause capacitor fee. If you gave it a different meaning from what it needs to be. Which is scope resolution. It would for one thing it's very hard to imagine what that meaning could be. So it's not overloadable. Another operator that's not overloadable is ternary operator and there is a single ternary operator and what I'm asking you is, to explain what it is and why it's not overloaded. Answer, the ternary operator is question mark equal so the typical thing is you have some lets write out something just so you remember, there are many people who will rarely use this operator. say something like (a < b)? a :b, so these are the three arguments, arg 1, arg 2, arg 3. The normal language says test this for truth. And true, this is the true result. And this is the false result. So, many people prefer to just do these things with, miscalculations. No reason that this is overloadable is [INAUDIBLE] didn't wanna bother with it. It's already not that used in operator. It's generally not an operator you'd expect in any algebra. It would be ugly, so it's not overloadable. Maybe there's some other reason. Okay, that's it for today, we've packed in a lot, and I will see you next time.