WEBVTT

00:00.150 --> 00:07.020
Hello again! In this video, we are going to look at interfaces and virtual functions. When we write a

00:07.020 --> 00:11.550
class hierarchy, we are usually modelling some kind of system, which contains various entities.

00:12.150 --> 00:17.880
We make the basic class some kind of generalized or abstracted version of these entities, which has

00:18.150 --> 00:20.070
all the things that these entities have in common.

00:20.760 --> 00:25.980
So for example, if we have graphical objects, we could have a shape class as the base. Or we could

00:25.980 --> 00:27.900
have a class like vehicle or animal.

00:29.190 --> 00:36.390
And then the derived classes in the hierarchy will represent concrete entities within the problem domain.

00:36.630 --> 00:40.050
So circle or aeroplane or cow, for example.

00:42.620 --> 00:49.070
The base class represents the interface to the hierarchy; the public virtual member functions will

00:49.400 --> 00:54.260
present all the functionality which the classes in the hierarchy have in common.

00:55.010 --> 01:00.500
If we have a hierarchy of graphical objects, then any kind of shape object can be drawn on the screen,

01:00.830 --> 01:03.020
rotated, moved around the screen, and so on.

01:04.760 --> 01:08.660
In the derived classes, we are going to override these virtual functions.

01:09.020 --> 01:14.600
We are going to implement them in a way which will be specific to that particular, concrete class.

01:15.170 --> 01:21.260
So for example, our circle class can draw a circle shape, rotate a circle, move a circle across

01:21.260 --> 01:22.460
the screen and so on.

01:23.870 --> 01:29.780
When we write our base class and we have to provide these public virtual member functions, we have a

01:29.780 --> 01:30.440
bit of a problem.

01:31.640 --> 01:34.880
Usually, these virtual functions cannot actually do anything useful.

01:35.390 --> 01:36.890
How would you draw a generic shape?

01:37.340 --> 01:39.590
What kind of food does a generic animal eat?

01:39.950 --> 01:41.720
How fast can a generic vehicle go?

01:42.530 --> 01:46.850
So one possibility is just to define these member functions with empty bodies.

01:47.540 --> 01:54.650
C++ does in fact provide a special syntax for this situation, which will say that these virtual functions

01:54.650 --> 01:59.450
are not implemented here, but they will be implemented in a child class. And that helps us express

01:59.450 --> 02:02.990
the idea of the base class as the interface to the hierarchy.

02:05.620 --> 02:07.780
The syntax for doing this is a bit strange.

02:08.290 --> 02:11.660
Instead of putting a pair of braces, you put " = 0".

02:12.250 --> 02:15.190
So, "virtual void draw() = 0".

02:16.120 --> 02:21.370
That means that draw is a so-called pure virtual function, which means it is not implemented in this

02:21.370 --> 02:21.760
class.

02:22.360 --> 02:28.930
It also means that the base class Shape is an abstract base class, so we cannot create any objects.

02:31.330 --> 02:36.910
So this makes it clear that the virtual function draw() is part of the interface, and not part of the implementation

02:36.910 --> 02:38.560
of a Shape object.

02:42.160 --> 02:48.190
So when we have a class with a pure virtual member function, we cannot instantiate it. So if we try to

02:48.190 --> 02:49.860
do that, we get a compiler error.

02:51.880 --> 02:56.350
This is known as an abstract base class, which is similar to "interfaces" in other languages.

02:57.690 --> 03:04.190
If we inherit from an abstract base class, then our child class must override all the pure virtual member

03:04.200 --> 03:11.820
functions that it inherits from the base class. If it does not do that, then it will have pure virtual

03:11.880 --> 03:16.560
member functions, which means that our class is itself going to be an abstract base class.

03:21.160 --> 03:23.950
So let's try this out, we have our familiar hierarchy.

03:25.060 --> 03:30.400
This time, the draw() member function in the base is going to be a pure virtual member

03:30.400 --> 03:32.380
function. Equals zero.

03:33.460 --> 03:38.790
And then, in the main function, we are just going to create an object of this class. So, er,

03:38.890 --> 03:42.370
well, I was going to ask you what happens, but if you can read, you already know!

03:44.690 --> 03:46.250
So there we are. We get a compiler error.

03:48.800 --> 03:51.020
Cannot instantiate abstract class.

03:53.680 --> 03:57.520
So when you have an abstract base cast, you cannot create any objects of that class.

04:00.450 --> 04:03.570
So let's comment this out for a minute, because it does not work.

04:06.600 --> 04:11.340
When we have derived classes, these are going to inherit the pure virtual functions, and they will be

04:11.340 --> 04:12.000
still pure.

04:12.180 --> 04:18.120
So if we have a derived class, which does not implement the pure virtual function, which does not override it,

04:18.990 --> 04:22.260
then that is going to be an abstract base class.

04:22.800 --> 04:26.070
So if we try to create an object of the Triangle class,

04:29.020 --> 04:30.250
what do you think will happen?

04:33.300 --> 04:37.630
We get the same compiler error again. Cannot instantiate abstract class.

04:40.260 --> 04:44.790
So this might all seem a bit elaborate, but there is actually one situation where it does help you.

04:45.600 --> 04:46.560
In C++,

04:46.560 --> 04:51.840
if you do not say that you are passing an argument by reference or by address, then it's passed by

04:51.840 --> 04:54.720
value, which means that the copy constructor is called.

04:55.440 --> 04:55.970
So let's say

04:55.970 --> 05:01.450
we have some code like this, where we meant to use dynamic binding, but we forgot to put in the reference

05:01.450 --> 05:02.820
or the pointer symbol.

05:04.570 --> 05:11.560
If we pass a Circle to this, then this Circle is going to be copied, the copy constructor called

05:11.560 --> 05:12.940
will be for the Shape object.

05:13.690 --> 05:19.720
So inside this function, we have a local variable s, which is a copy of the Circle object, but it only

05:19.720 --> 05:21.130
has the Shape part of the object.

05:21.520 --> 05:25.450
So it is as if the Circle part of the object had been sliced off.

05:26.560 --> 05:28.660
And this is known as "object slicing".

05:30.720 --> 05:32.280
So let's look at an example of this.

05:32.550 --> 05:37.440
We've gone back to using a normal, virtual draw() function in the base class.

05:37.470 --> 05:38.970
So this is not pure virtual.

05:39.300 --> 05:42.510
This is an ordinary base class and not an abstract base class.

05:43.560 --> 05:47.630
Then we have our draw_shape(), where we forgot to put the reference symbol in.

05:48.600 --> 05:54.000
Then we have a main function which creates a Circle object and passes it to this draw_shape() function.

05:54.960 --> 05:56.430
So what do you think is going to happen?

06:00.380 --> 06:03.440
So that calls the draw member function of the Shape class.

06:05.550 --> 06:10.200
And you may say, "Ah, yes, but that's because you are not using dynamic binding here. You should be using a

06:10.200 --> 06:11.190
reference or a pointer".

06:12.180 --> 06:14.070
OK, so let's try using a pointer.

06:18.300 --> 06:20.020
(Yep, great C++ syntax!)

06:21.920 --> 06:22.750
And there we are.

06:22.910 --> 06:28.940
We still get the draw() member function of Shape, so that means that the dynamic type of the variable s

06:28.940 --> 06:31.230
is is actually a Shape class.

06:32.180 --> 06:34.550
And that is because the copy constructor for Shape was called.

06:35.300 --> 06:37.910
So if we now make this a pure virtual function...

06:41.940 --> 06:43.100
(Must remember to make this const!)

06:48.620 --> 06:55.390
And now we get a compiler error, and can you guess what the compiler error is? "Cannot instantiate

06:55.390 --> 06:56.420
abstract class".

06:59.810 --> 07:02.090
OK, so that tells us that we are doing something wrong.

07:03.160 --> 07:05.290
So, oh yes, we need to use a reference here, don't we?

07:10.140 --> 07:12.780
And then it compiles. And then it does use dynamic binding.

07:13.440 --> 07:20.430
So in this case, we are passing Shape by reference. We get the dynamic type and we are calling through a reference.

07:25.490 --> 07:30.470
Okay, so just to quickly sum that up. An abstract base class cannot be passed by value.

07:31.160 --> 07:36.140
If you have code like this and Shape is an abstract base class, then this does not compile.

07:36.740 --> 07:39.770
So you are forced to pass it by reference or by address.

07:40.280 --> 07:43.790
And then when you do that, s will have the dynamic type.

07:44.150 --> 07:50.810
of the original argument, so Circle or whatever. And then when you call it through that reference or through

07:50.810 --> 07:56.870
the pointer, then you will get the member function of the original object, using dynamic binding.

07:58.050 --> 08:00.120
OK, so that is it for this feature.

08:00.600 --> 08:01.490
I will see you next time.

08:01.710 --> 08:03.720
Until then, keep coding!
