invoking method of a base class in a derived class

Typically, if your specialized implementations in derived class need to reuse the base class's generic implementation, you can use the scope resolution operator ::.

e.g. Using scope resolution operator :: to invoke base class Functions from derived class and main(). demolist10_5

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

class Fish
{
private:
bool isFreshWaterFish;

public:
Fish(bool isFreshWater) : isFreshWaterFish(isFreshWater) {}
void Swim()
{
if (isFreshWaterFish)
cout << "Lake" << endl;
else
cout << "ocean" << endl;
}
};

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

class Crap : public Fish
{
public:
Crap() : Fish(true){};
void Swim()
{
cout << "Crap part" << endl;
Fish::Swim();
}
};

int main()
{
Tuna MyLunch;
Crap MyDinner;

cout << "call base class from derived class" << endl;
MyDinner.Swim();
cout << "call base class from main()" << endl;
MyLunch.Fish::Swim();

return 0;
}
Analysis

: line 37 demonstrates calling the base class function Fish::Swim() using ::, on the other hand, line 49 in main invoke base class method from main().

derived class hiding base class's methods

overriding can take an extreme form where Tuna::Swim() can potentially hide all overloaded versions of Fish::Swim() available, even causing compilation failure when overloaded ones are used.

e.g. Tuna::Swim() hides overloaded method Fish::Swim() demolist10_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
32
33
34
35
36
37
38
#include <iostream>
using namespace std;

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

void Swim(bool isFreshWaterFish)
{
if (isFreshWaterFish)
cout << "from lake" << endl;
else
cout << "from ocean" << endl;
}
};

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

int main()
{
Tuna Mylunch;
Mylunch.Swim();//expected output swim fast
Mylunch.Fish::Swim();//expected output happy fish
Mylunch.Fish::Swim(false);//expected output from ocean

return 0;
}
Analysis

: Line 33-35 demonstrates three ways to invoke Fish::Swim() and Tuna::Swim() properly. In addition, another 2 ways to invoke Fish::Swim() via an instance of Tuna as follow:

  • use keyword using in class Tuna to unhide Swim() in class Fish

    1
    2
    3
    4
    5
    6
    7
    8
    9
    class Tuna : public Fish
    {
    public:
    using Fish::Swim;//unhide all Swim() methods in class Fish
    void Swim()
    {
    cout << "swim fast" << endl;
    }
    }
  • override all overloaded Swim() in class Tuna

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    class Tuna : public Fish
    {
    public:
    void Swim(bool isFreshWaterFish)
    {
    if (isFreshWaterFish)
    cout << "from lake" << endl;
    else
    cout << "from ocean" << endl;
    }

    void Swim()
    {
    cout << "swim fast" << endl;
    }
    }

order of constructor

Base class objects are instantiated before derived class. Thus, the Fish part of Tuna is constructed first, so that the protected and public members of Fish are ready for consumption when Tuna is instantiated. Within the instantiation of Fish and Tuna, the members attributes are instantiated before the constructor Fish::Fish() invoked, ensuring that members attributes are ready before the constructor work with them.

order of destructor

When Tuna goes out of scope, the sequence of destruction is the opposite to that of construction, It goes as this order that derived classes are destructed before base class.

e.g. the order of constructor and destructor of base class, derived class and members thereof demolist10_7

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
58
59
60
61
62
63
64
65
66
#include <iostream>
using namespace std;

class FishDummyMember
{
public:
FishDummyMember()
{
cout << "FishDummyMember construtor" << endl;
}
~FishDummyMember()
{
cout << "FishDummyMember destructor" << endl;
}
};
class Fish
{
protected:
FishDummyMember dummy;//invoke it using 'Fish::dummy'

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

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

class Tuna : public Fish
{
private:
TunaDummyMember dummy;

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

int main()
{
Tuna TestOne;

return 0;
}

output:

1
2
3
4
5
6
7
8
FishDummyMember construtor
Fish constructor
TunaDummyMember constructor
Tuna constructor
Tuna destructor
TunaDummyMember destructor
Fish destructor
FishDummyMember destructor
Analysis

: As output demonstrated, when an object of class Tuna instantiated, instantiation actually start at the top of the hierarchy. Therefore, the base class Fish part of class Tuna is instantiated first, and in doing so, the member of Fish, Fish::dummy is instantiated first before Tuna's constructor. Notice that Fish::dummy is an object of class FishDummyMember. After these, the instantiation of Tuna continues first with instantiation of member Tuna::dummy finally followed by the execution of the constructor code in Tuna::Tuan(). On the contrast, the sequence of destructor is exactly the opposite.