Introduction
The virutal keyword is very versatile. A really short and nasty summary is, it is to ensure the correct functions or base class is inherited / loaded during runtime.
Virtual Functions
In general computer science, dispatching is the action of selecting which polymorphic function to call [2]. In C++, there are static dispatching (like templates) and dynamic dispatching (polymorphism). In static dispatching, the dispatched function is known during compile time, whereas in dynamic dispatching, the dispatched function won’t be known until the runtime type of an object is known. This way, polymorphism could become useful when we have different types of objects with inheritance, and want to call their corresponding versions of a function.
The mechanism of dynamic dispatching in C++ is through a virtual function table (vtable), which stores pointers to all virtual functions’ addresses. Vtable will only be looked up when polymorphism is used.
By declaring a function virtual
in base class, one should provide overrride
functions in subclasses if they want to apply polymorphism on the function. One example is:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <string>
class B{
public:
virtual void foo() { std::cout << "Hello, I'm B "<<std::endl;}
};
class C: public B{
public:
void foo() override { std::cout << "Hello, I'm C"<<std::endl;}
};
class D: public B{
};
int main()
{
// See Hello, I'm C
C().foo();
// See Hello, I'm B
D().foo();
}
If You Have A Base Class, Always Define A Virtual Dtor For It
In C++ polymorphism, IT IS REQUIRED TO DECLARE BASE CLASS’S DTOR AS VIRTUAL
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <memory>
struct Shape {
// Omitting virtual destructor will skip the dtor of Circle as well.
virtual ~Shape() { std::cout << "Deleting shape" << std::endl;};
};
struct Circle : public Shape {
~Circle() {
std::cout << "Circle destructor called" << std::endl;
}
};
int main() {
Shape* shape = new Circle();
delete shape; // Won't see "Circle destructor called"
return 0;
}
Because this way, the Vtable is able to include the proper destructors.
1
2
[&~Base::Base]
[&~Derived::Derived]
If the base class doesn’t have a virtual dtor:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <memory>
struct Shape {
// Omitting virtual destructor will skip the dtor of Circle as well.
~Shape() { std::cout << "Deleting shape" << std::endl;};
};
struct Circle : public Shape {
~Circle() {
std::cout << "Circle destructor called" << std::endl;
}
};
int main() {
Shape* shape = new Circle();
delete shape; // Won't see "Circle destructor called"
return 0;
}
- One weirdity here is if ~Base::Base is marked virtual, Derived::Derived is automatically virtual
Quirks
- Virtual functions cannot be called in ctor or dtor
- This is because in construction or destruction, when calling a virtual function, C++ does NOT use dynamic dispatch there, and will only use the definition from the class where the constructor is defined. This is to prevent calling a virtual function that touches uninitialized data. See below example
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
class Base {
public:
Base() {
display();
}
virtual ~Base() = default;
virtual void display() const {
std::cout << "Display from Base" << std::endl;
}
virtual void displayer_wrapper() const{
display();
}
};
class Derived : public Base {
public:
Derived() {}
~Derived() override = default;
void display() const override {
std::cout << "Display from Derived" << std::endl;
}
};
int main() {
// See "Display from Base", because it's called in base ctor and polymorphism is banned at the point.
Base* d = new Derived();
// See "Display from Derived", which is expected from polymorphism
d -> displayer_wrapper();
delete d;
return 0;
}
Virtual Inheritance
The Dreadful Diamond on Derivation Problem
TODO
References
- Stack Overflow explanation on Virtual inheritance: stackoverflow.com/a/21607/5652483
- Wikipedia on Dynamic Dispatch: https://en.wikipedia.org/wiki/Dynamic_dispatch