Help with inheritance and virtual functions

Im trying to learn how to use virtual functions and inheritance, i folloiwed an example but made my own classes and functions, when i compile the program i get no errors but when the console window starts it crashes immediatley, not sure whats wrong.

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
// deriv_VirtualFunctions.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;

class Actor
{
    public:
        Actor(float actorHealth, string actorName, float actorAttackPower): actorHealth(100), actorName("Default_Actor"), actorAttackPower(0) {}
        ~Actor();

        virtual string GetActorName(){ return actorName; }
        virtual float GetActorHealth(){ return actorHealth; }
        virtual float GetActorAttackPower(){ return actorAttackPower; }

    private:
        float actorHealth;
        string actorName;
        float actorAttackPower;
};


class Velociraptor : public Actor
{
    public:
        Velociraptor(float actorHealth, string actorName, float actorAttackPower): Actor(actorHealth, actorName, actorAttackPower) {}
        string GetActorName(){  }
        float GetActorHealth(){  }
        float GetActorAttackPower(){  }
};

class TRex : public Actor
{
    public:
        TRex(float actorHealth, string actorName, float actorAttackPower): Actor(actorHealth, actorName, actorAttackPower){}
        string GetActorName(){  }
        float GetActorHealth(){  }
        float GetActorAttackPower(){  }
};

class Player : public Actor
{
    public:
        Player(float actorHealth, string actorName, float actorAttackPower): Actor(actorHealth, actorName, actorAttackPower) {}
        string GetActorName(){ }
        float GetActorHealth(){ }
        float GetActorAttackPower(){ }
};

int main()
{
   // Create objects of type CheckingAccount and SavingsAccount.
   Velociraptor *pVelociraptor = new Velociraptor(100, "Velociraptor", 30);
   TRex *pTRex = new TRex(100, "T-Rex", 37);

   Actor *pActor = pVelociraptor;
   pActor->GetActorName();
}
Compile with warnings enabled and pay attention to the warnings.

In particular:

warning: no return statement in function returning non-void (GNU)
warning: control reaches end of non-void function (LLVM)
http://coliru.stacked-crooked.com/a/7964a2df24da688b

The microsoft compiler actually generates an error for this (this is good):
For instance, error: 'Velociraptor::GetActorName': must return a value
http://rextester.com/DKM64997
Ok i fixed it but it keeps giving me the name of the default actor from the base class instead of "Velociraptor"

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
// deriv_VirtualFunctions.cpp
// compile with: /EHsc
#include <iostream>
using namespace std;

class Actor
{
    public:
        Actor(float actorHealth, string actorName, float actorAttackPower): actorHealth(100), actorName("Default_Actor"), actorAttackPower(0) {}
        ~Actor();

        virtual string GetActorName(){ return actorName; }
        virtual float GetActorHealth(){ return actorHealth; }
        virtual float GetActorAttackPower(){ return actorAttackPower; }

    protected:
        float actorHealth;
        string actorName;
        float actorAttackPower;
};


class Velociraptor : public Actor
{
    public:
        Velociraptor(float actorHealth, string actorName, float actorAttackPower): Actor(actorHealth, actorName, actorAttackPower) {}
        string GetActorName(){ return actorName; }
        float GetActorHealth(){ return actorHealth; }
        float GetActorAttackPower(){ return actorAttackPower; }
};

class TRex : public Actor
{
    public:
        TRex(float actorHealth, string actorName, float actorAttackPower): Actor(actorHealth, actorName, actorAttackPower){}
        string GetActorName(){ return actorName; }
        float GetActorHealth(){ return actorHealth; }
        float GetActorAttackPower(){ return actorAttackPower; }
};

class Player : public Actor
{
    public:
        Player(float actorHealth, string actorName, float actorAttackPower): Actor(actorHealth, actorName, actorAttackPower) {}
        string GetActorName(){ return actorName; }
        float GetActorHealth(){ return actorHealth; }
        float GetActorAttackPower(){ return actorAttackPower; }
};

int main()
{
   // Create objects of type CheckingAccount and SavingsAccount.
   Velociraptor *pVelociraptor = new Velociraptor(100, "Velociraptor", 30);
   //TRex *pTRex = new TRex(100, "T-Rex", 37);

   Actor *pActor = pVelociraptor;
   cout << pActor->GetActorName();
}
> it keeps giving me the name of the default actor from the base class instead of "Velociraptor"

Because the override Velociraptor::GetActorName() returns the name of the actor in the base class,
which always is "Default_Actor".

Repeat: pay attention to the warnings:
In constructor 'Actor::Actor(float, std::__cxx11::string, float)':
warning: unused parameter 'actorName' [-Wunused-parameter]
         Actor(float actorHealth, string actorName, float actorAttackPower): actorHealth(100), actorName("Default_Actor"), actorAttackPower(0) {}
                                         ^~~~~~~~~

http://coliru.stacked-crooked.com/a/7964a2df24da688b

Try this:
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
#include <iostream>
#include <string> // required

class Actor
{
    public:
        Actor( std::string name ) : name_(name) {}
        virtual ~Actor() = default ;
        virtual std::string name() const { return name_ ; }

    private: std::string name_ ;
};


class Velociraptor : public Actor
{
    public:
        using Actor::Actor ;
        virtual std::string name() const override { return Actor::name() + " (veclociraptor)" ; }
};

class TRex : public Actor
{
    public:
        using Actor::Actor ;
        virtual std::string name() const override { return Actor::name() + " (trex)" ; }
};

class Player : public Actor
{
    public:
        using Actor::Actor ;
        virtual std::string name() const override { return Actor::name() + " (player)" ; }
};

int main()
{
    const Velociraptor vel( "Athos" ) ;
    const TRex rex( "Porthos" ) ;
    const Player plyr( "Aramis" ) ;

    const Actor* actors[] = { std::addressof(vel), std::addressof(rex), std::addressof(plyr) } ;
    for( const Actor* a : actors ) std::cout << ( a ? a->name() : "nullptr" ) << '\n' ;
}

http://coliru.stacked-crooked.com/a/568ec7d396f71291
Edit: I had some interruptions so it took a long time to reply, JLBorges always provides better advice anyway :+)

Ch1156 wrote:
Ok i fixed it but it keeps giving me the name of the default actor from the base class instead of "Velociraptor"


Hi,

This happens because of line 56 where you assign to a pointer of base class type. There isn't really a need for that, a pointer to derived class can be a substitute for a base class pointer already.

The normal way of doing polymorphism is to say specify a container of base class pointers:

std::vector<Actor*> AllTheActors;

Then fill it with pointers to derived:

1
2
AllTheActors.push_back(&Velociraptor); // adress of with & operator, don't use new
// others here 


Then do a range for loop so that each one calls a function:

1
2
3
for (const auto& item : AllTheActors ) {
      item->GetActorName();
}



The other thing is that all of your functions are identical, there is no need for that - they are inherited.

Usually one defines pure virtual functions in the base class, these form the interface. These functions are over-ridden in the derived classes ; it's a good idea to use the override keyword.

As I mentioned in the comments, don't use new. It looks as though you have used it purely to obtain a pointer (unnecessary), but new has other problems. Mainly that it will leak memory if some thing throws an exception, the destructor is never reached.

Perhaps a good example to study is one involving shapes: have the derived classes functions do the right thing for calculating area and perimeter for different shapes such as rectangle, triangle, regular polygons etc. This might better demonstrate how functions with the same name do different things for derived classes.

Good Luck!!
Last edited on
Hi

Just some further comments about polymorphism:

The main idea is that derived classes should exhibit different behavior. In the Shapes example, the various area functions have different formulae to calculate the areas for rectangles, triangles, polygons etc. So this is a clear example of how derived class functions are overridden.

With an Animal example, IMO it's not so clear. Sure we can have a speak function for each animal, and have that output some thing different for each one ; But the same effect could be achieved by there being a member variable called std::string Sound; I think your code as it stands now suffers from the same problem: We could achieve the same thing with a std::vector<Actor> , and not derive any classes.

Another example with animals that does exhibit different behavior, might be the concept of movement. Say we have different movement modes for different animals: Bipedal Animals might have Walk, Jog, Run, Sprint ; some quadrupedal animals might have Walk, Trot, Canter, Gallop. So Fred.move(2) results in "Fred the human is jogging" , while Pharlap.move(2) results in "Pharlap the horse is trotting". Also associated with this is the speeds: A human jogging is a different speed to a horse trotting.

Note that the move function is different for each derived class: Human's move function might call anyone of these functions: Walk() ; Jog() ; Run() ; Sprint() while Horse move function could call any of these: Walk(), Trot(), Canter(), Gallop() . These functions should be private.

Another way to think of this is that we want to avoid any conditional statements based on the type of the derived class. That is, we don't want this pseudo code:

if (Human ) {
  if (mode == 1 ) Walk();
  else if (mode == 2 ) Jog();
}
else if (Horse) {
   if (mode == 1 ) Walk();
   else if (mode == 2 ) Trot();
}
}


What we do want, is the ability to add new derived classes, and only provide the overridden functions for it - not change anything else. Closely related to this is concept of Design Patterns, but that is more advanced.

Another example might be an Attack function: The Human shoots a pistol (Human::Attack() calls Shoot() ); The Trex bites (Trex::Attack() calls Bite() ) ; The MudSlinger throws mud (MudSlinger::Attack() calls ThrowMud() ) These functions should be private, we don't anybody to call them.

I hope this clears things up a bit, look forward to seeing any new code have.

Regards :+)

Sorry for the late response. So could you show me a full working example of what you think is the perfect example of polymorphism and multiple inheritance? Having some code to look at and dissect would help me understand better i think, like a few classes with some virtual functions to show inheritance and polymorphism.
Last edited on
bump.
So could you show me a full working example of what you think is the perfect example of polymorphism and multiple inheritance?

I’m sure someone could give you a full example of the perfect example, but at the moment it seems you need to be content with one basic, dirty example like the following (no multiple inheritance, anyway):
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
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
#include <iostream>

class Actor { // abstract base class
public:
    Actor(double health_arg = 100.0, double attack_arg = 0.0, 
            std::string whatis_arg = "Dinosaur");
    std::string getName() const { return whatis; }
    double getHealth() const { return health; }
    double getAttack() const { return attack; }
protected:
    double health {},
           attack {};
    std::string whatis;
    virtual void calculateAttack() = 0;
    friend std::ostream& operator<<(std::ostream& os, const Actor* rhs)
    {
        return os << rhs->whatis << ": health: " << rhs->health 
                  << "; attack: " << rhs->attack;
    }
};

Actor::Actor(double health_arg, double attack_arg, std::string whatis_arg)
    : health { health_arg }, attack { attack_arg }, whatis { whatis_arg }
{}


class Velociraptor : public Actor {
public:
    Velociraptor(int teeth_arg = 75, int pack_arg = 0, double health_arg = 80.0, 
                    double attack_arg = 0, std::string whatis_arg = "Velociraptor");
protected:
    void calculateAttack() override;
private:
    int teeth,
        pack;
    friend std::ostream& operator<<(std::ostream& os, const Velociraptor* rhs)
    {
        return os << reinterpret_cast<const Actor*>(rhs) 
                  << " (further details: teeth: " << rhs->teeth 
                  << "; member of a pack of " << rhs->pack << ')';
    }
};

Velociraptor::Velociraptor(int teeth_arg, int pack_arg, double health_arg, 
                            double attack_arg, std::string whatis_arg)
    : Actor(health_arg, attack_arg, whatis_arg)
{
    teeth = teeth_arg;
    pack = pack_arg;
    calculateAttack();
}

void Velociraptor::calculateAttack()
{
    // attack --> given value + teeth + number of element in the pack * 2
    //            (the more they are, the more brave each one is)
    attack += teeth + pack * 2;
}

class TRex : public Actor {
public:
    TRex(double weight_arg = 1000.00, int hunger_arg = 0, double health_arg = 200.00, 
            double attack_arg = 0.0, std::string whatis_arg = "Tyrannosaurus Rex");
protected:
    void calculateAttack() override;
private:
    double weight;
    int hunger;
    friend std::ostream& operator<<(std::ostream& os, const TRex* rhs)
    {
        return os << reinterpret_cast<const Actor*>(rhs) 
                  << " (further details: weight: " << rhs->weight 
                  << "; it hasn't eaten for " << rhs->hunger << " days)";
    }
};

TRex::TRex(double weight_arg, int hunger_arg, double health_arg, 
            double attack_arg, std::string whatis_arg)
    : Actor(health_arg, attack_arg, whatis_arg)
{
    weight = weight_arg;
    hunger = hunger_arg;
    calculateAttack();
}

void TRex::calculateAttack()
{
    // attack -> given value + 10% * weight + hunger
    //           (it's big, it needs to eat everyday)
    attack += weight / 10 + hunger;
}

class Player : public Actor {
public:
    Player(int ammunition_arg = 0, double intelligence_arg = 100.0, 
            double health_arg = 20.0, double attack_arg = 0.0, 
            std::string whatis_arg = "Morituro");
protected:
    void calculateAttack() override;
private:
    int ammunition;
    double intelligence;
    friend std::ostream& operator<<(std::ostream& os, const Player* rhs)
    {
        return os << reinterpret_cast<const Actor*>(rhs) 
                  << " (further details: ammunition: " << rhs->ammunition 
                  << "; intelligence " << rhs->intelligence;
    }
};

Player::Player(int ammunition_arg, double intelligence_arg, double health_arg,
            double attack_arg, std::string whatis_arg)
    : Actor(health_arg, attack_arg, whatis_arg)
{
    ammunition = ammunition_arg;
    intelligence = intelligence_arg;
    calculateAttack();
}

void Player::calculateAttack()
{
    // attack -> given value + 50% * intelligence + ammunition * 3
    attack += intelligence / 2 + ammunition * 3;
}

void waitForEnter();

int main()
{
    // Actor actor; <-- error: you can't have an instance of a abstract base class
    Actor* vel = new Velociraptor; // but you can have a pointer of type Actor*
    Actor* tyr = new TRex(1212.37, 5, 160, 13, "Fat Rex");
    Actor* play = new Player;
    std::cout << "Characteristic are:\n" << vel << '\n' << tyr << '\n' << play << '\n';
    // Anyway, if you use the proper pointers, the compiler can understand better
    // what you want:
    Velociraptor* vel2 = reinterpret_cast<Velociraptor*>(vel);
    TRex* tyr2 = reinterpret_cast<TRex*>(tyr);
    Player* play2 = reinterpret_cast<Player*>(play);
    std::cout << "\nCharacteristic are:\n" << vel2 << '\n' << tyr2 << '\n' << play2 << '\n';
    waitForEnter();
    return 0;
}

void waitForEnter()
{
    std::cout << "\nPress ENTER to continue...\n";
    std::cin.ignore(std::numeric_limits<std::streamsize>::max(), '\n');
}

Topic archived. No new replies allowed.