1 00:00:05,360 --> 00:00:08,885 In this video, we'll learn what polymorphism is and how it 2 00:00:08,940 --> 00:00:12,190 can help us better develop our object-oriented applications. 3 00:00:13,349 --> 00:00:16,999 Polymorphism is a fundamental part of object-oriented programming. 4 00:00:17,770 --> 00:00:21,230 There are several types of polymorphism in c++, and we've 5 00:00:21,230 --> 00:00:24,310 already learned about two of them when we overloaded functions and 6 00:00:24,310 --> 00:00:25,700 when we overloaded operators. 7 00:00:26,630 --> 00:00:29,760 These are considered forms of compile-time polymorphism. 8 00:00:30,320 --> 00:00:34,709 You might see the terms compile time, early binding and static binding. 9 00:00:35,139 --> 00:00:36,750 These terms all mean the same thing. 10 00:00:36,950 --> 00:00:39,620 They simply mean before the program executes. 11 00:00:40,050 --> 00:00:42,650 In other words, something the compiler takes care of 12 00:00:42,750 --> 00:00:44,290 way before the program runs. 13 00:00:44,300 --> 00:00:50,429 Runtime, late binding and dynamic binding all refer to the association 14 00:00:50,610 --> 00:00:54,719 of what the programmer wants to do with how to do it but at 15 00:00:55,110 --> 00:00:56,670 runtime as the program executes. 16 00:00:57,560 --> 00:01:00,760 We'll see some examples in a few slides that will make this very clear. 17 00:01:01,980 --> 00:01:05,899 Polymorphism allows us to think more abstractly when we write programs. 18 00:01:06,240 --> 00:01:10,579 We can think deposit or print or draw instead of thinking in specific 19 00:01:10,580 --> 00:01:12,770 versions of deposit, print and draw. 20 00:01:13,290 --> 00:01:17,120 So I can simply think deposit a 1000 dollars to this account. 21 00:01:17,700 --> 00:01:20,120 And I don't have to worry about what kind of count it is. 22 00:01:20,470 --> 00:01:24,370 I'll be sure that the correct deposit function will be called depending 23 00:01:24,370 --> 00:01:25,910 on what type of account I have. 24 00:01:26,139 --> 00:01:27,910 And this is all determined at Runtime. 25 00:01:28,359 --> 00:01:31,890 In c++ the default type of binding is static binding. 26 00:01:32,310 --> 00:01:36,020 This makes sense since c++ is all about efficiency and static 27 00:01:36,020 --> 00:01:40,280 binding is done before runtime so programs can be as fast as possible. 28 00:01:41,010 --> 00:01:44,940 However, sometimes we want to defer decisions until run time. 29 00:01:45,549 --> 00:01:50,189 In c++ we can opt into this behavior by using base class pointers 30 00:01:50,189 --> 00:01:53,990 or references and declaring our functions as virtual functions. 31 00:01:54,619 --> 00:01:57,710 We'll see the details and syntax of this later in this section. 32 00:01:58,080 --> 00:02:02,710 But first, let's look at the types of polymorphism that c++ provides so that 33 00:02:02,740 --> 00:02:04,690 we can put this section in context. 34 00:02:06,539 --> 00:02:09,729 Here's a simple chart that shows the types of polymorphism in c++. 35 00:02:11,080 --> 00:02:14,720 There are two types of polymorphism, compile-time and runtime. 36 00:02:15,510 --> 00:02:18,780 Compile-time polymorphism includes what we've already learned in 37 00:02:18,780 --> 00:02:21,710 this course, function overloading and operator overloading. 38 00:02:22,440 --> 00:02:25,640 We also have runtime polymorphism, and that's what this section 39 00:02:25,640 --> 00:02:27,030 of the course will focus on. 40 00:02:27,550 --> 00:02:31,390 We achieve runtime polymorphism by overriding functions and 41 00:02:31,390 --> 00:02:35,329 using inheritance, using virtual functions and then having base 42 00:02:35,330 --> 00:02:36,939 class pointers and references. 43 00:02:37,730 --> 00:02:40,510 I think the best way to understand dynamic polymorphism 44 00:02:40,639 --> 00:02:42,120 is to use a simple example. 45 00:02:45,049 --> 00:02:49,380 First, let's look at a non-polymorphic example that uses static binding. 46 00:02:50,179 --> 00:02:53,030 In this case, we have an account hierarchy using public 47 00:02:53,030 --> 00:02:55,729 inheritance as you can see on the right side of the slide. 48 00:02:56,549 --> 00:02:59,619 We'll assume that every type of account class has its own version 49 00:02:59,619 --> 00:03:02,750 of the withdraw method and each withdraw method is different 50 00:03:02,920 --> 00:03:04,310 depending on the type of account. 51 00:03:05,010 --> 00:03:07,850 So let's create four objects, one for each type of account 52 00:03:07,880 --> 00:03:09,269 and call their withdraw methods. 53 00:03:09,920 --> 00:03:15,199 When we call account a's withdraw method, it will 54 00:03:15,230 --> 00:03:16,600 call accounts withdraw. 55 00:03:17,079 --> 00:03:19,690 This makes sense since a is an account object. 56 00:03:20,380 --> 00:03:23,210 The compiler knows this and binds this method call at 57 00:03:23,280 --> 00:03:24,410 compile-time or statically. 58 00:03:26,080 --> 00:03:28,850 The same applies to objects b c and d. 59 00:03:29,400 --> 00:03:32,449 In each case, the compiler is binding the calls to withdraw 60 00:03:32,710 --> 00:03:35,729 based on the information that it has about the objects when they 61 00:03:35,730 --> 00:03:37,129 were declared in the source code. 62 00:03:37,650 --> 00:03:41,950 So b withdrawal calls savings withdraw, c withdraw calls 63 00:03:42,070 --> 00:03:45,540 checking withdraw, and d withdraw calls trust withdraw. 64 00:03:46,080 --> 00:03:47,049 So far so good. 65 00:03:47,259 --> 00:03:49,049 It's what we've done and what we expect. 66 00:03:49,469 --> 00:03:51,510 But now let's take a look at the pointer p. 67 00:03:52,660 --> 00:03:54,800 P is a pointer to an account object. 68 00:03:55,080 --> 00:03:57,579 So p contains the address of an account object. 69 00:03:58,430 --> 00:04:01,190 Now we create a trust account dynamically on the heap and 70 00:04:01,190 --> 00:04:02,510 assign its address to p. 71 00:04:03,130 --> 00:04:04,030 Is this legal? 72 00:04:04,710 --> 00:04:08,779 Sure, p can hold addresses of accounts and trust is an account. 73 00:04:09,049 --> 00:04:10,829 The inheritance hierarchy tells us so. 74 00:04:11,670 --> 00:04:14,010 So what happens when we call the withdrawal method for 75 00:04:14,010 --> 00:04:15,620 the object pointed to by p? 76 00:04:16,070 --> 00:04:20,070 Well, we're using static binding by default so the compiler doesn't 77 00:04:20,089 --> 00:04:23,450 really know what type of account object p is pointing to at runtime. 78 00:04:23,460 --> 00:04:24,530 It doesn't really care. 79 00:04:25,049 --> 00:04:28,029 All it knows is that p is pointing to an account. 80 00:04:28,420 --> 00:04:30,610 So it will call the account withdraw method. 81 00:04:31,300 --> 00:04:33,360 This is probably not what we expected. 82 00:04:33,670 --> 00:04:36,430 It certainly isn't what we wanted since we want the trust 83 00:04:36,480 --> 00:04:39,590 object on the heap to use its own version of withdraw. 84 00:04:40,469 --> 00:04:42,919 Please be sure that you understand this simple example 85 00:04:43,070 --> 00:04:46,360 since it's fundamental to understanding dynamic polymorphism. 86 00:04:46,809 --> 00:04:51,090 Remember, all the compiler knows is that p is a pointer to an account 87 00:04:51,320 --> 00:04:54,320 so it will bind the withdraw method to the account classes 88 00:04:54,330 --> 00:04:55,900 withdraw method at compile time. 89 00:04:56,300 --> 00:04:59,400 Let's see another problem with static binding in the same context. 90 00:05:01,340 --> 00:05:03,680 In this example, we have the same class hierarchy. 91 00:05:04,020 --> 00:05:07,169 And let's suppose that each account class has its own version 92 00:05:07,170 --> 00:05:11,000 of a display method that displays account information based on 93 00:05:11,000 --> 00:05:12,270 the type of account we have. 94 00:05:12,980 --> 00:05:18,159 So let's create a simple c++ function called display account, and it expects 95 00:05:18,179 --> 00:05:19,940 a reference to an account object. 96 00:05:20,830 --> 00:05:25,239 Since all our derived classes are accounts, we can pass any of 97 00:05:25,240 --> 00:05:28,840 them into this function, and this function will call the display method 98 00:05:28,840 --> 00:05:30,430 for the account object passed in. 99 00:05:31,310 --> 00:05:36,340 So you can see that the code creates four objects: a b c and d, one for 100 00:05:36,360 --> 00:05:40,250 each type of account and then it calls display account and passes in each 101 00:05:40,250 --> 00:05:41,660 of these objects to the function. 102 00:05:42,609 --> 00:05:45,010 The behavior you get from the display account function 103 00:05:45,250 --> 00:05:46,770 may not be what you expect. 104 00:05:47,210 --> 00:05:50,750 But by default c++ is doing exactly what it's supposed 105 00:05:50,760 --> 00:05:52,490 to do, static binding. 106 00:05:53,120 --> 00:05:57,360 When the compiler sees the call to acc.display in the display 107 00:05:57,360 --> 00:06:01,030 account function, it will bind the call to accounts display. 108 00:06:02,060 --> 00:06:05,320 So accounts display method will be called regardless of what 109 00:06:05,350 --> 00:06:08,859 object was passed in, and our display will only display whatever 110 00:06:08,860 --> 00:06:10,249 is in the account part of us. 111 00:06:10,990 --> 00:06:14,849 There is a way for c++ to ask the account object being passed in, 112 00:06:15,200 --> 00:06:16,750 hey, what kind of account are you. 113 00:06:17,190 --> 00:06:20,230 And then depending on that, we can have if else statements that call 114 00:06:20,230 --> 00:06:21,820 the appropriate display methods. 115 00:06:22,310 --> 00:06:25,630 That's bad coding practice, and it also makes us program less 116 00:06:25,670 --> 00:06:29,069 abstractly since then we've got to figure out what kind of object we've 117 00:06:29,070 --> 00:06:31,000 got and then call its functions. 118 00:06:31,330 --> 00:06:32,270 There's a better way. 119 00:06:32,679 --> 00:06:34,870 That's where runtime polymorphism comes in. 120 00:06:35,340 --> 00:06:39,790 Let's see how this same example works with runtime or dynamic polymorphism. 121 00:06:41,890 --> 00:06:45,010 Now let's see the same example using polymorphic functions. 122 00:06:45,830 --> 00:06:49,739 Here we see the same account class hierarchy except that this time we've 123 00:06:49,780 --> 00:06:52,140 opted into runtime polymorphism. 124 00:06:52,850 --> 00:06:55,670 We'll see how in a few videos, but notice that the withdraw 125 00:06:55,700 --> 00:06:58,740 methods in the class diagram are now virtual functions. 126 00:06:59,730 --> 00:07:03,090 This allows us to use runtime polymorphism when using base 127 00:07:03,090 --> 00:07:04,669 class pointers or references. 128 00:07:05,719 --> 00:07:07,559 Let's look at the same example as before. 129 00:07:07,860 --> 00:07:11,289 Notice that the four count objects: a b c and d, have 130 00:07:11,289 --> 00:07:12,760 exactly the same behavior. 131 00:07:13,000 --> 00:07:16,640 Since we're not using base class pointers or references, these 132 00:07:16,640 --> 00:07:19,810 examples are all statically bound just like before. 133 00:07:20,809 --> 00:07:22,929 But now let's look at the pointer p. 134 00:07:23,790 --> 00:07:26,470 In this case, when we call the withdraw method of the object 135 00:07:26,580 --> 00:07:30,219 pointed to by p, the trust withdraw method will be called. 136 00:07:30,910 --> 00:07:32,619 That's what we want and that's pretty cool. 137 00:07:33,050 --> 00:07:37,260 The idea of using virtual functions tells the compiler not to bind the 138 00:07:37,260 --> 00:07:41,330 calling compile time but instead defer the binding to runtime. 139 00:07:42,200 --> 00:07:45,509 And at runtime, a check will occur to see exactly what p 140 00:07:45,509 --> 00:07:49,100 is pointing to and then that object's method will be called. 141 00:07:49,389 --> 00:07:52,210 In this case, the trust withdraw method. 142 00:07:52,870 --> 00:07:54,130 Let's see the other example. 143 00:07:56,380 --> 00:08:00,349 Again, now we have virtual display functions in our account class. 144 00:08:00,780 --> 00:08:03,800 So whenever we use a pointer or a reference to a base 145 00:08:03,800 --> 00:08:07,670 class, the binding is done at runtime, not at compile time. 146 00:08:08,370 --> 00:08:11,950 So now we create four objects: a b c and d, each as a 147 00:08:11,950 --> 00:08:13,070 different type of account. 148 00:08:13,840 --> 00:08:16,960 Now when we pass these objects to the display account function, 149 00:08:17,219 --> 00:08:22,190 the binding of acc.display will take place at runtime and call 150 00:08:22,190 --> 00:08:25,430 the display method based on the type of object being passed in. 151 00:08:25,890 --> 00:08:28,120 That's pretty cool and very, very powerful. 152 00:08:28,880 --> 00:08:32,500 I can now write functions, methods and data structures that use 153 00:08:32,500 --> 00:08:36,770 pointers and references to base class objects, and I know that the correct 154 00:08:36,820 --> 00:08:38,320 methods will be called at runtime. 155 00:08:38,860 --> 00:08:41,280 Remember, that account class hierarchy we did in the last 156 00:08:41,289 --> 00:08:44,759 sections challenge, it used static binding since we hadn't learned 157 00:08:44,769 --> 00:08:46,676 about dynamic polymorphism yet. 158 00:08:46,676 --> 00:08:49,380 Remember, all those utility functions that we wrote, that 159 00:08:49,380 --> 00:08:50,630 were all pretty much the same. 160 00:08:51,240 --> 00:08:53,370 Now we could write just one version, and they'll work with 161 00:08:53,370 --> 00:08:56,180 all the account types once we convert that account hierarchy 162 00:08:56,410 --> 00:08:57,910 to use polymorphic functions. 163 00:08:58,720 --> 00:08:59,420 Fantastic. 164 00:08:59,630 --> 00:09:02,100 In the next video we'll go over how we can use base class 165 00:09:02,110 --> 00:09:03,859 pointers in a little more detail. 166 00:09:05,340 --> 00:09:08,609 Before we do the next video, let's go over here to the IDE 167 00:09:08,610 --> 00:09:09,850 and walk through this problem. 168 00:09:10,050 --> 00:09:12,260 This is the same problem we had with the account class. 169 00:09:12,559 --> 00:09:15,150 And what I'm doing here I'm just creating a base and a derived class 170 00:09:15,150 --> 00:09:18,410 to absolutely show you and walk you through what's going on here. 171 00:09:18,900 --> 00:09:24,430 I'm in the section 16 workspace in the problem project so. 172 00:09:24,430 --> 00:09:25,130 What have I got? 173 00:09:25,170 --> 00:09:28,710 Well, you can see here it's can't get much simpler than this hierarchy. 174 00:09:29,030 --> 00:09:33,049 I've got a base class right here, and it's got one public 175 00:09:33,049 --> 00:09:34,479 method called say hello. 176 00:09:35,020 --> 00:09:35,570 That's all. 177 00:09:35,770 --> 00:09:37,700 And all it says is hello I'm a base class. 178 00:09:38,300 --> 00:09:41,910 Then I've got a derived class, that's derived publicly from base. 179 00:09:42,480 --> 00:09:47,099 And it's got the same method void say hello, but it says 180 00:09:47,219 --> 00:09:49,379 hello I'm a derived class object. 181 00:09:50,650 --> 00:09:51,430 That's it. 182 00:09:51,570 --> 00:09:53,490 Then I've got this function right here that I've written. 183 00:09:53,500 --> 00:09:56,640 This is not a member function, this is just a regular c++ function. 184 00:09:56,640 --> 00:10:02,250 It's called greetings, and it expects a base object by reference. 185 00:10:02,429 --> 00:10:04,680 Now remember, a derived is a base. 186 00:10:04,700 --> 00:10:07,570 So I can pass in either a derived object or a base 187 00:10:07,570 --> 00:10:08,690 object to this function. 188 00:10:09,230 --> 00:10:11,970 And this function is simply going to say greetings, and then 189 00:10:11,970 --> 00:10:15,189 it's going to call the specific objects say hello method. 190 00:10:16,020 --> 00:10:17,220 So what do we expect here? 191 00:10:17,480 --> 00:10:20,350 Well, the idea would be that if I have a derived object and i pass 192 00:10:20,350 --> 00:10:23,790 it into this function right here, I'd say greetings and then hello 193 00:10:23,790 --> 00:10:26,650 I'm a derived class object, but that's not what happens because 194 00:10:26,830 --> 00:10:28,050 we're doing static binding. 195 00:10:28,350 --> 00:10:29,849 So let's walk through this example. 196 00:10:29,859 --> 00:10:32,750 It's really important that you understand the static binding 197 00:10:32,750 --> 00:10:36,020 version before we start talking about the dynamic binding version. 198 00:10:37,160 --> 00:10:37,810 Here's my main. 199 00:10:38,050 --> 00:10:41,220 Let's create a base object, we'll call it b, and we'll 200 00:10:41,220 --> 00:10:42,870 simply call b.say hello. 201 00:10:44,799 --> 00:10:45,889 What do we expect here? 202 00:10:45,969 --> 00:10:47,619 Exactly what you would think, right. 203 00:10:47,900 --> 00:10:51,340 Let me just scroll up just a little bit, and I'm the compiler. 204 00:10:52,010 --> 00:10:53,220 I'm looking in here. 205 00:10:54,270 --> 00:10:57,650 There is no virtual -- we haven't really talked about virtual yet, but 206 00:10:57,650 --> 00:11:02,239 you'll see the word virtual in here when we buy into dynamic polymorphism. 207 00:11:02,480 --> 00:11:04,429 So the compiler sees this class hierarchy. 208 00:11:04,609 --> 00:11:07,420 It doesn't see anything virtual so there's not going to be 209 00:11:07,480 --> 00:11:09,730 any dynamic polymorphism here. 210 00:11:09,730 --> 00:11:11,900 It knows that it needs to bind everything statically. 211 00:11:11,900 --> 00:11:14,380 And also, we're not coming at this with a base pointer. 212 00:11:14,380 --> 00:11:17,170 We have to have a base pointer, and we'll talk a little bit about base 213 00:11:17,170 --> 00:11:19,030 pointer more in the next video. 214 00:11:19,030 --> 00:11:21,270 So this is really simple for the compiler, right. 215 00:11:21,670 --> 00:11:23,390 B.say hello okay. 216 00:11:23,390 --> 00:11:25,880 Which say hello method do I bind? 217 00:11:25,900 --> 00:11:26,740 This guy, right here. 218 00:11:27,120 --> 00:11:27,770 How does it know? 219 00:11:27,770 --> 00:11:29,590 Well, b is a base. 220 00:11:30,080 --> 00:11:32,399 That's all the compiler knows is this right here. 221 00:11:32,679 --> 00:11:33,690 So that's what it's using. 222 00:11:33,950 --> 00:11:36,530 And that's just going to say hello I'm a base class object. 223 00:11:37,000 --> 00:11:40,159 And let's do the same thing before we run it, we'll do it again, and we'll 224 00:11:40,160 --> 00:11:42,420 do it this time for a derived object. 225 00:11:42,790 --> 00:11:45,869 So let's do that, we'll say derived d. 226 00:11:47,110 --> 00:11:52,310 And we'll say d.say hello again, same exact thing happens. 227 00:11:52,540 --> 00:11:56,880 The compiler knows that d is a type of derived, right. 228 00:11:56,880 --> 00:11:58,070 Its type is derived. 229 00:11:58,379 --> 00:12:01,790 So in this case, it's going to bind this method right here. 230 00:12:02,820 --> 00:12:04,480 Again, there's no pointers. 231 00:12:04,520 --> 00:12:08,450 When there's no pointers and no virtual methods, everything is static. 232 00:12:08,740 --> 00:12:12,880 So if we run this, what we expect is we expect this say hello to 233 00:12:12,880 --> 00:12:16,835 say hi I'm a base class object and this say hello here on line 29 to 234 00:12:16,840 --> 00:12:18,530 say hi I'm a derived class object. 235 00:12:18,750 --> 00:12:19,580 So let's run that. 236 00:12:20,670 --> 00:12:24,080 And there's the output, hello I'm a base class object and 237 00:12:24,080 --> 00:12:27,010 hello I'm a derived class object, exactly as we expect. 238 00:12:28,280 --> 00:12:31,930 Okay, so now let's run into some of the the issues that we'll run 239 00:12:31,930 --> 00:12:36,020 into when we start using base class pointers and references. 240 00:12:36,240 --> 00:12:38,089 Why don't we do this function first right here. 241 00:12:38,949 --> 00:12:40,919 So suppose that I want to call that function. 242 00:12:40,919 --> 00:12:42,899 Remember, this is a regular c++ function. 243 00:12:43,129 --> 00:12:46,659 I'm going to call greetings, and I'll pass in the base class to it. 244 00:12:47,229 --> 00:12:49,439 I can do that because b is a base. 245 00:12:49,999 --> 00:12:51,499 And then I'm going to call it again. 246 00:12:51,899 --> 00:12:53,049 And I'm going to pass in d. 247 00:12:53,049 --> 00:12:57,210 I can do that because a derived is a base, right. 248 00:12:57,210 --> 00:12:59,280 That's what my inheritance hierarchy is doing for me. 249 00:12:59,680 --> 00:13:04,710 So what I expect here would be for the b to say greetings hello I'm 250 00:13:04,710 --> 00:13:08,080 a bass and the d to say greetings hello I'm a derived, but that's 251 00:13:08,080 --> 00:13:10,689 not what happens because we've got static binding going on. 252 00:13:10,879 --> 00:13:13,599 So let's run this, and you'll see the problem. 253 00:13:15,280 --> 00:13:17,459 There you go, you can see that right there those two 254 00:13:17,460 --> 00:13:18,430 greetings is right here. 255 00:13:18,450 --> 00:13:20,520 There's the output greetings, hello I'm a bass. 256 00:13:20,540 --> 00:13:23,270 And then greetings, hello I'm a bass again even though I'm 257 00:13:23,270 --> 00:13:24,760 passing or derived into it. 258 00:13:25,520 --> 00:13:28,530 And we know now that the reason that this is happening is I'll 259 00:13:28,539 --> 00:13:31,060 scroll up just a little bit is right here is the problem. 260 00:13:32,470 --> 00:13:34,770 We're not using dynamic polymorphism. 261 00:13:34,920 --> 00:13:37,910 And as far as the compiler is concerned, obj is 262 00:13:37,910 --> 00:13:39,110 a reference to a base. 263 00:13:39,110 --> 00:13:39,980 That's all it knows. 264 00:13:40,000 --> 00:13:41,040 That's all it cares about. 265 00:13:41,320 --> 00:13:48,340 So right here that will always bind to base say hello, and that's 266 00:13:48,340 --> 00:13:49,760 why we're seeing that behavior. 267 00:13:51,500 --> 00:13:55,120 Okay, the same thing happens when we've got a base pointer. 268 00:13:56,550 --> 00:13:57,889 And let's do that really quickly. 269 00:13:58,200 --> 00:14:01,160 So suppose we have a pointer, and we'll just call it ptr. 270 00:14:01,850 --> 00:14:05,820 And in this case, ptr is a pointer to a base object, 271 00:14:05,820 --> 00:14:07,150 right, an object of that type. 272 00:14:07,510 --> 00:14:09,240 And we can say new derived. 273 00:14:10,770 --> 00:14:13,490 Now this sometimes confuses students, but this makes perfect 274 00:14:13,490 --> 00:14:16,699 sense, right, because think about it sometimes you think about the 275 00:14:16,710 --> 00:14:17,980 hierarchy, you get all confused. 276 00:14:17,990 --> 00:14:19,600 Think about what this really says. 277 00:14:19,950 --> 00:14:26,339 This really says that ptr can hold the address of any base object, right. 278 00:14:26,740 --> 00:14:29,640 Well derived is a base object. 279 00:14:30,170 --> 00:14:32,710 A derived is a base that's what the inheritance is telling me. 280 00:14:32,710 --> 00:14:35,810 So what happens here is here's ptr. 281 00:14:37,090 --> 00:14:41,829 Here is my derived object that I'm creating on the heap, right. 282 00:14:41,869 --> 00:14:43,780 And there's really nothing in there right now because there's 283 00:14:43,780 --> 00:14:47,790 no attributes or anything, but this guy is pointing here, and 284 00:14:47,790 --> 00:14:50,900 that's okay because this is a base. 285 00:14:51,920 --> 00:14:53,609 So that's valid. 286 00:14:53,610 --> 00:14:55,080 The compiler is happy about that. 287 00:14:55,980 --> 00:15:01,930 But the problem comes in now when I try to call ptr say hello. 288 00:15:04,030 --> 00:15:07,760 We've got no dynamic polymorphism, no polymorphic functions. 289 00:15:07,900 --> 00:15:11,319 So what's going to happen is the compiler sees ptr 290 00:15:11,319 --> 00:15:13,009 is a pointer to a base. 291 00:15:13,070 --> 00:15:13,990 It's all it knows. 292 00:15:14,000 --> 00:15:18,889 So it's going to bind this to base hello, always. 293 00:15:22,410 --> 00:15:24,470 So we'll always bind that to base say hello. 294 00:15:25,200 --> 00:15:28,020 And again, this is probably not what we want. 295 00:15:28,070 --> 00:15:32,486 We want dynamic polymorphism to figure out what I'm pointing to at 296 00:15:32,486 --> 00:15:34,220 runtime and call that appropriately. 297 00:15:34,359 --> 00:15:36,420 So let's run this one last time. 298 00:15:37,280 --> 00:15:40,060 And let's see if we get a run here, we do. 299 00:15:40,590 --> 00:15:41,220 So here we go. 300 00:15:41,220 --> 00:15:42,520 Let's walk through this one more time. 301 00:15:42,520 --> 00:15:45,010 Here's b.say hello, hello on the base. 302 00:15:45,400 --> 00:15:47,610 There's d.say hello, hello I'm a drive. 303 00:15:47,620 --> 00:15:49,030 That's all using static binding. 304 00:15:49,030 --> 00:15:50,050 That's perfectly fine. 305 00:15:50,610 --> 00:15:53,810 Now here I'm passing in a b and a d to this method greetings. 306 00:15:54,299 --> 00:15:57,339 In both cases, the compiler doesn't know anything except that 307 00:15:57,340 --> 00:16:00,630 that's a base, so it's binding it to the say hello right here 308 00:16:01,170 --> 00:16:03,270 on line 21 of the base class. 309 00:16:03,940 --> 00:16:07,689 And then here's my pointer, and I'm calling say hello through the pointer. 310 00:16:07,740 --> 00:16:10,360 Again, even though I'm pointing to a derived object 311 00:16:10,379 --> 00:16:11,530 the compiler doesn't care. 312 00:16:12,129 --> 00:16:14,940 This is not being deferred to runtime so the compiler is binding 313 00:16:14,940 --> 00:16:17,079 it right now at compile time. 314 00:16:18,000 --> 00:16:21,800 Okay, this also -- you don't have to use raw pointers for this. 315 00:16:21,800 --> 00:16:24,170 We haven't talked about smart pointers yet but we will. 316 00:16:24,580 --> 00:16:29,569 And suppose I include memory that's where the smart pointers live. 317 00:16:30,520 --> 00:16:33,400 And I could scroll down here, and I could say something -- like let 318 00:16:33,400 --> 00:16:37,570 me just close this up a little, I could say something like a std unique 319 00:16:37,570 --> 00:16:46,639 pointer, and we can have a unique pointer to a base, and we can call 320 00:16:46,639 --> 00:16:53,900 this ptr1, and then we could call make unique, which creates that pointer and 321 00:16:53,900 --> 00:16:55,939 the pointer is a derived this time. 322 00:16:57,550 --> 00:16:59,600 And we'll just empty constructor right here. 323 00:16:59,679 --> 00:17:03,370 So that'll create a smart pointer this -- we'll talk about smart pointers later. 324 00:17:03,710 --> 00:17:07,210 And then we could just say ptr1, and we can call the say 325 00:17:07,210 --> 00:17:08,720 hello method there, right. 326 00:17:08,720 --> 00:17:11,079 Now obviously we've got a pointer here so. 327 00:17:11,079 --> 00:17:14,650 We really should free or delete that pointer just so we are 328 00:17:14,929 --> 00:17:16,920 consistent with best practices here. 329 00:17:17,290 --> 00:17:19,148 We don't have to delete smart pointers, they 330 00:17:19,210 --> 00:17:20,339 take care of themselves. 331 00:17:20,569 --> 00:17:25,290 So if we run this, you can see that even with a smart pointer, there's 332 00:17:25,290 --> 00:17:29,090 a regular pointer, there's a smart pointer, they're both being statically 333 00:17:29,120 --> 00:17:30,460 bound, just like you would expect. 334 00:17:31,430 --> 00:17:33,670 All right, so in the next video, we'll talk about this base 335 00:17:33,670 --> 00:17:35,990 class pointer and see a little bit more about how that works.