1 00:00:05,250 --> 00:00:08,250 In this video, we'll talk about virtual destructors. 2 00:00:09,150 --> 00:00:10,700 In the previous example, 3 00:00:10,700 --> 00:00:14,800 we got four warning messages when we compiled our simple account class hierarchy. 4 00:00:15,600 --> 00:00:17,850 These warnings were the compiler telling us 5 00:00:17,850 --> 00:00:20,050 that deleting a polymorphic object 6 00:00:20,050 --> 00:00:24,150 that doesn't have a virtual destructor could lead to unexpected behavior. 7 00:00:24,810 --> 00:00:27,610 In fact, if we have a derived class object 8 00:00:27,610 --> 00:00:30,610 that's destroyed by deleting its base class pointer 9 00:00:30,610 --> 00:00:33,170 and that class does not have a virtual destructor, 10 00:00:33,170 --> 00:00:36,470 then the behavior is undefined by the c++ standard. 11 00:00:36,720 --> 00:00:37,720 That's not good. 12 00:00:38,120 --> 00:00:38,780 Remember, 13 00:00:38,780 --> 00:00:41,580 derived objects must always be destroyed in the correct 14 00:00:41,580 --> 00:00:43,880 order and starting at the correct destructor. 15 00:00:44,280 --> 00:00:46,080 So how can we address these warnings. 16 00:00:47,680 --> 00:00:50,180 Thankfully, the solution is simple and intuitive. 17 00:00:50,180 --> 00:00:53,540 If we have a class and that class has virtual functions, 18 00:00:53,740 --> 00:00:56,740 then that class must always provide a virtual destructor. 19 00:00:57,530 --> 00:01:00,530 Once we provide a virtual destructor in the base class, 20 00:01:00,530 --> 00:01:03,830 then all destructors in the derived classes are also virtual. 21 00:01:03,830 --> 00:01:08,530 We don't have to provide the virtual keyword again, although it's best practice to do so. 22 00:01:09,430 --> 00:01:11,930 Here is the code snippet for the account base class. 23 00:01:12,730 --> 00:01:15,730 Since this class has a virtual function withdraw, 24 00:01:15,980 --> 00:01:18,880 then we must declare the destructor to be virtual as well. 25 00:01:19,680 --> 00:01:23,280 That's it. Simple solution to a potentially serious situation. 26 00:01:23,940 --> 00:01:27,190 Let's head over to the IDE and see what happens on my system 27 00:01:27,190 --> 00:01:31,450 when we don't provide virtual destructors, and then we'll add them and see the difference. 28 00:01:32,110 --> 00:01:35,880 Welcome back. So I'm in the IDE. I'm in the section 16 workspace 29 00:01:35,880 --> 00:01:38,540 in the VirtualDestructors project. 30 00:01:39,640 --> 00:01:42,340 So we've got the same account class we've had all along. 31 00:01:42,340 --> 00:01:45,220 And what I've done here is I've added destructors. 32 00:01:45,220 --> 00:01:49,100 Now these are not virtual destructors yet, they're just plain old destructors. 33 00:01:49,100 --> 00:01:51,800 So you can see here's the destructor for the account class, 34 00:01:51,800 --> 00:01:54,300 and it's simply printing out account destructor. 35 00:01:54,660 --> 00:01:58,260 The checking account displays checking destructor, savings instructor 36 00:01:58,260 --> 00:02:00,460 and trust destructor. Okay. 37 00:02:00,460 --> 00:02:02,960 And now we've got our same example that we've been using. 38 00:02:03,460 --> 00:02:06,460 We're creating four pointers to a base class object. 39 00:02:06,710 --> 00:02:10,960 We're creating an account, a savings account, a checking account,a trust account dynamically on the heap. 40 00:02:11,320 --> 00:02:13,120 And then we're calling the withdraw method. 41 00:02:13,120 --> 00:02:15,110 All that will work perfectly fine right now. 42 00:02:15,110 --> 00:02:19,100 The problem comes in right here when we delete those pointers, right. 43 00:02:19,100 --> 00:02:23,000 We're going to free the storage for them. And when we build this project, 44 00:02:23,000 --> 00:02:26,200 we're going to run into the same four warnings that we had before. 45 00:02:26,200 --> 00:02:30,700 The warnings are about the leading object of polymorphic class type account, 46 00:02:30,700 --> 00:02:34,500 which has non-virtual destructor might cause undefined behavior. 47 00:02:34,500 --> 00:02:38,500 It sounds pretty ominous, and it's a potentially serious warning actually. 48 00:02:38,800 --> 00:02:42,700 All the compiler knows is that we've got pointers to account objects. 49 00:02:42,700 --> 00:02:45,690 So it really doesn't know which destructor to call. 50 00:02:45,690 --> 00:02:48,890 And this is undefined in the c++ standard. 51 00:02:48,890 --> 00:02:51,880 If we run this, we'll get one behavior on this compiler. 52 00:02:52,280 --> 00:02:56,580 And this is pretty standard. You can see what's happening here when I'm calling the delete 53 00:02:57,080 --> 00:02:59,080 right here on the four pointers, 54 00:02:59,080 --> 00:03:01,280 we're getting account, destructor account, destructor. 55 00:03:01,280 --> 00:03:04,280 So you can see that the base class destructor is being called. 56 00:03:04,280 --> 00:03:06,160 Now that's a real problem because 57 00:03:06,160 --> 00:03:09,460 p1 -- that's okay for p1, right. P1 is an account, 58 00:03:09,460 --> 00:03:13,160 but p2 right now points to a savings account, p3 is a checking, 59 00:03:13,160 --> 00:03:14,460 p4 is a trust. 60 00:03:14,460 --> 00:03:17,160 So there could be a lot of memory leaks going on. 61 00:03:17,160 --> 00:03:20,520 Those destructors maybe are closing files 62 00:03:20,520 --> 00:03:24,020 and writing buffers out, and none of that stuff is being called. 63 00:03:24,020 --> 00:03:26,620 So potentially, we could have a serious problem here. 64 00:03:26,980 --> 00:03:31,080 Thankfully, the fix is really easy. All we need to do is provide 65 00:03:31,480 --> 00:03:35,740 virtual destructors. And it's really easy to do. We can just use the virtual keyword 66 00:03:35,740 --> 00:03:37,540 right in front of the destructor. 67 00:03:37,540 --> 00:03:40,240 And we only have to do it here in the base class. 68 00:03:40,240 --> 00:03:43,040 But as I mentioned, it's good practice to do it everywhere. 69 00:03:43,590 --> 00:03:47,250 So there are my virtual destructors. Let me just finish up, 70 00:03:48,050 --> 00:03:50,850 one more to go, and here we go. 71 00:03:50,850 --> 00:03:53,650 Now if we run this -- now if we build and run, 72 00:03:55,150 --> 00:03:59,410 you'll see we get no errors. And now watch what happens, this is more like it, right. 73 00:03:59,410 --> 00:04:04,010 Here we're deleting p1, which calls the account destructor as before. 74 00:04:04,370 --> 00:04:07,070 Now when we delete p2, p2 is a savings. 75 00:04:07,070 --> 00:04:10,730 So you can see that the savings destructor is being called followed by 76 00:04:10,730 --> 00:04:14,030 the base class destructor, which is exactly what we want. 77 00:04:14,030 --> 00:04:17,230 So if we're freeing memory in those 78 00:04:17,230 --> 00:04:22,130 destructors, if we're closing files, writing buffers, taking care of resources, 79 00:04:22,330 --> 00:04:25,990 now everything's going to work as we want. You can see p3 here 80 00:04:25,990 --> 00:04:30,590 is doing the checking destructor followed by the account destructor and then following 81 00:04:30,590 --> 00:04:33,790 the trust destructor and the account destructor for p4. 82 00:04:33,790 --> 00:04:38,090 Remember, we're only going one deep here in the inheritance hierarchy. 83 00:04:38,090 --> 00:04:40,290 If you're going 5 6 10 deep, 84 00:04:40,290 --> 00:04:42,940 then you've got a lot of destructors that weren't called. 85 00:04:42,940 --> 00:04:44,240 They will be called now. 86 00:04:44,240 --> 00:04:47,040 They'll take care of memory, take care of resources and so forth. 87 00:04:47,370 --> 00:04:48,970 The rule is really, really simple. 88 00:04:49,330 --> 00:04:52,530 If you've got a class and it's got a virtual function, 89 00:04:52,530 --> 00:04:54,750 give it a virtual destructor as well. 90 00:04:54,750 --> 00:04:57,750 Sometimes students ask about virtual constructors, 91 00:04:58,150 --> 00:05:00,350 you can't have virtual constructors. 92 00:05:00,350 --> 00:05:02,350 They're not allowed, and they really don't make any sense at all. 93 00:05:02,850 --> 00:05:05,350 Okay. So that's it about virtual destructors. 94 00:05:05,350 --> 00:05:09,450 In the next video, what we'll do is we'll talk about the override specifier 95 00:05:09,450 --> 00:05:12,950 that gets the compiler to help us out just in case we make some errors.