Virtual methods

Polymorphism

Polymorphism means 'multiple forms'. Let us say we define the following classes: Object, Weapon, Sword and DoubleEdgedSword. The class DoubleEdgedSword inherits from Sword, Sword inherits from Weapon and Weapon inherits from Object. If we define the same method, let us says printInfo, in the classes Object, Weapon and Sword, then this method exists in three forms: Object::printInfo, Weapon::printInfo and Sword::printInfo. It is therefore a polymorphic method. Let us define those four classes and those 3 method forms:

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#include <iostream> class Object { public: void printInfo() { std::cout << "This is an object.\n"; } }; class Weapon : public Object { public: void printInfo() { std::cout << "This is a weapon.\n"; } }; class Sword : public Weapon { public: void printInfo() { std::cout << "This is a sword.\n"; } }; class DoubleEdgedSword : public Sword { }; int main() { Object o; Weapon w; Sword s; DoubleEdgedSword d; o.printInfo(); w.printInfo(); s.printInfo(); d.printInfo(); std::cout << "\n"; w.Object::printInfo(); s.Weapon::printInfo(); d.Object::printInfo(); return 0; }

By default, the version (form) of the method from the class of the object it is called from is used, unless it is not defined in that class, in which case the version of the closest parent is used. It is also possible to specify which version to use by prefixing the name of the method with the namespace of the class.

Now, let us call the methods printInfo from the objects, through a pointer of type Object:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int main() { Object o; Weapon w; Sword s; DoubleEdgedSword d; Object *ptr = &o; ptr->printInfo(); ptr = &w; ptr->printInfo(); ptr = &s; ptr->printInfo(); ptr = &d; ptr->printInfo(); return 0; }

Every time, no matter if the class of the pointed object defines an other form of the method printInfo, the version of the class of the pointer is used. What if we wanted the version of the method from the pointed object to be called? It can be achieved by declaring the methods as virtual.

The keyword virtual

The result of the last example is probably not what we want. By default, when we call a method through a pointer, there is no verification to check whether it points to a child object with a different version of the method, or not. However, by making a method virtual, by simply adding the keyword virtual, before the return type of a method, in its declaration, the version (form) of the method from pointed object will be used when it is called through a reference or a pointer. Note that if a method is declared (Not only defined), then the keyword virtual must only be written in the declaration of the method and not in its definition, otherwise it will cause a compilation error. Also, it is not required to put the keyword virtual in the declaration of every method. When a method from a parent class is declared as virtual, all other forms from child classes become virtual.

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream> class Object { public: // That method is virtual. virtual void printInfo() { std::cout << "This is an object.\n"; } }; class Weapon : public Object { public: // Because Object::printInfo is virtual, that method is also virtual. void printInfo() { std::cout << "This is a weapon.\n"; } }; class Sword : public Weapon { public: // Because Weapon::printInfo is virtual, that method is also virtual. void printInfo() { std::cout << "This is a sword.\n"; } }; class DoubleEdgedSword : public Sword { }; int main() { Object o; Weapon w; Sword s; DoubleEdgedSword d; Object *ptr = &o; ptr->printInfo(); ptr = &w; ptr->printInfo(); ptr = &s; ptr->printInfo(); ptr = &d; ptr->printInfo(); return 0; }

In the example above, because the method is defined as virtual, even though the method of the objects is called through a pointer of parent class type, the result is the same as if it was directly called from the object.

Note that virtual methods are a bit slower to call than normal methods, because a verification of the class of the pointed object must be done. However, that does not mean we should not use them. They are very useful and coding the equivalent of what they are doing without using them would probably not be faster.

Here is an example where a virtual method is defined outside of its class definition:

1
2
3
4
5
6
7
8
9
10
11
12
class Object { public: // Declaration of a virtual method. virtual void printInfo(); }; // Definition of a virtual method. void Object::printInfo() { std::cout << "This is an object.\n"; }