basics of polymorphism

e.g. invoking methods using an instance of Base class which belongs to Derived class. demolist11_1

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
#include <iostream>
using namespace std;

class Fish
{
public:
void Swim()
{
cout << "fish can swim" << endl;
}
};

class Tuna : public Fish
{
public:
void Swim()
{
cout << "tuna can swim" << endl;
}
};

void SetFishSwim(Fish &inputFish)
{
//calling Fish::Swim()
inputFish.Swim();
};

int main()
{
Tuna TunaOne;
TunaOne.Swim();
SetFishSwim(TunaOne);

return 0;
}

Line 32 passes TunaOne as a parameter to Method FishSwim() that interprets it as a reference Fish &. The output as follow:

1
2
tuna can swim
fish can swim

Apparently, FishSwim doesn't care if the object sent was a Tuna, handles it as a Fish and invokes Fish::Swim().

polymorphic behavior implemented using virtual functions

Syntax

:

1
2
3
4
5
6
7
8
9
class Base
{
virtual ReturnType FunctionName (Parameter List);
};

class Derived
{
ReturnType FunctionName (Parameter List);
}

use of keyword virtual means that the compiler ensure that any overriding variant of the requested base class method is invoked .

e.g The effect of declaring Fish::Swim as a virtual method

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
#include <iostream>
using namespace std;

class Fish
{
public:
virtual void Swim()
{
cout << "fish can swim" << endl;
}
};

class Tuna : public Fish
{
public:
virtual void Swim()
{
cout << "tuna can swim" << endl;
}
};

void SetFishSwim(Fish &inputFish)
{
//calling Fish::Swim()
inputFish.Swim();
};

int main()
{
Tuna TunaOne;
TunaOne.Swim();
SetFishSwim(TunaOne);

return 0;
}

output

1
2
tuna can swim
tuna can swim

The output it produces dramatically different, Fish::Swim has not been invoked because of the presence of overriding variants Tuna::Swim that has priority over Fish::Swim() because the latter has declared as a virtual function.

need for virtual destructor

What happen when a function calls operator delete using a pointer type Base* which actually points to an instance of type Derived?

e.g. A function that invoke operator delete on Base* demolist11_3

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
#include <iostream>
using namespace std;

class Fish
{
public:
Fish()
{
cout << "fish constructor" << endl;
}
~Fish()
{
cout << "fish destructor" << endl;
}
};

class Tuna : public Fish
{
public:
Tuna()
{
cout << "tuna constructor" << endl;
}
~Tuna()
{
cout << "tuna destructor" << endl;
}
};

void DelMemory(Fish *pFish)
{
delete pFish;
};

int main()
{
cout << "allocate memory for Tuna object" << endl;
Tuna *pTuna = new Tuna;
cout << "delete Tuna" << endl;
DelMemory(pTuna);

cout << "instantiating a Tuna on stack" << endl;
Tuna myTuna;
cout << "automatically remove destructor and constructor" << endl;

return 0;
}

Output

1
2
3
4
5
6
7
8
9
10
11
allocate memory for Tuna object
fish constructor
tuna constructor
delete Tuna
fish destructor
instantiating a Tuna on stack
fish constructor
tuna constructor
automatically remove destructor and constructor
tuna destructor
fish destructor

Note that in line 40 while Fish and Tuna were both constructed on the free store due to new, the destructor of Tuna was not invoked during the delete. The flaw means that the destructor of a deriving class that has been instantiated on the free store using new would not be invoked if delete is called using a pointer of type Base*. To avoid this problem, using virtual destructor.

e.g. using virtual destructors to ensure that the destructors in derived classes are invoked when deleting a pointer of type Base*.

modifying demolist11_3 to the following in line 11

1
virtual ~Fish()

output

1
2
3
4
5
6
7
8
9
10
11
12
allocate memory for Tuna object
fish constructor
tuna constructor
delete Tuna
tuna destructor
fish destructor
instantiating a Tuna on stack
fish constructor
tuna constructor
automatically remove destructor and constructor
tuna destructor
fish destructor

Note that this small change resulted in the compiler essentially executing Tuna::~Tuna() in addition to Fish::~Fish(), when operator delete was invoked on Fish which actually pointed to Tuna.

Note

Always declare the base destructor as virtual:

1
2
3
4
5
6
7
8
class Base
{
public:
virtual ~Base()
{
//...implementation
}
}

This ensures that one with a pointer Base* cannot invoke delete in a way that instances of derived class are not correctly destroyed.

abstract base classes and pure virtual function

pure virtual method Syntax

1
2
3
4
5
class AbstractBase
{
public:
virtual void DoSomething() = 0;//pure virtual function
};

DoSomething() need to be implemented by the class which derived from AbstractBase. That is shown in the following:

1
2
3
4
5
6
7
8
class Derived : public AbstractBase
{
public:
void DoSomething() // pure virtual function must be implemented
{
cout <<"inplemented virtual function"<<endl;
}
};

class Fish as an abstract base class for Tuna demolist11_6

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
#include <iostream>
using namespace std;

class Fish
{
public:
virtual void Swim() = 0;
};

class Tuna : public Fish
{
public:
void Swim()
{
cout << "Tuna swim fast" << endl;
}
};

void TestSwim(Fish &urType)
{
urType.Swim();
}

int main()
{
//Fish myFish; //fails cannot instantiate an abstract base class
Tuna myTuna;
TestSwim(myTuna);

return 0;
}

Abstract Base Classes are often called ABCs

if uncomment line 26, error occur as shown. Line 19-22 demonstrates that even if an ABCs can not be instantiated, you can use it as a reference or a pointer.