Inheritance problem

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
#include <iostream>

using namespace std;

class Character
{
    public:
        void display()
        {
            cout << health;
        }

    protected:
        Character(int n) : health(n) {}
        int health;
};

class Player : public Character
{
    public:
        Player() : Character(1) {}
        Player(int x) : Character(x) {}

        void attack(Enemy e)
        {
            e.health--;
        }
};

class Enemy : public Character
{
    public:
        Enemy() : Character(1) {}
        Enemy(int x) : Character(x) {}

        void attack(Player p)
        {
            p.health--;
        }
};

int main()
{
    Player p(5);
    Enemy e(5);

    return 0;
}

Could someone explain to me why this doesn't work? I thought protected inheritance of members meant the derived class could use them, but I'm getting errors.

I also tried making the two attack() functions a part of the Character class, but then I got an error saying something along the lines of 'Player/Enemy not declared'.

Any help would be appreciated.
Last edited on
Why not put in the Character class The attack method like this
1
2
3
4
5
void attack(Character c)
{
  c.health--;
}


But delete it from both the derived classes..

To Player Class the health member of Enemy is inaccessible and vice versa..
24:21: error: 'Enemy' has not been declared

 In member function 'void Player::attack(int)':
26:15: error: request for member 'health' in 'e', which is of non-class type 'int'

 In member function 'void Enemy::attack(Player)':
15:13: error: 'int Character::health' is protected
38:15: error: within this context

We have no idea what an "Enemy" is before line 30. Hence the line 24 and 26 errors. A cyclic dependency. You could place implementation of Player::attack() after definition of class Enemy.

Line 38: Member of Enemy attempts to change internals of a Player object. Player IS-A Character, just like Enemy IS-A Character, but does the "protected" allow access For Enemy to anywhere else than the Character subobject within Enemy objects?


Why a Character cannot attack a Character without subclasses?
Last edited on
@obscure
Thank you, what you suggested works. I just had to pass the object by reference to make any changes made to the members stick.
1
2
3
4
void attack(Character &c)
{
    c.health--;
}


@keskiverto
Why a Character cannot attack a Character without subclasses?

The idea is that I will expand on this and give Player and Enemy objects their own unique behaviour later on. Examples might be that the player can equip weapons and armour and enemies have a rage meter fill up the more damage they take in battle. But before I get to any of that, I have to actually understand the language mechanics properly :)

EDIT:
but does the "protected" allow access For Enemy to anywhere else than the Character subobject within Enemy objects?

I'm not sure if I understand you right. Do you mean that derived classes have full access to the protected members of their base class, but only for their own objects? And that you are not allowed to access the protected members of another class object, even if both classes are derived from the same base class?
Last edited on
Your observations imply a "yes" to my question, don't they?

With class hierarchy one usually encounters virtual member functions and abstract base classes.
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
class Character
{
    public:
        Character(int n) : health(n) {}
        virtual ~Character();
        void attack(Character & victim);

    private:
        int health;
        virtual int attackimp() = 0; // pure virtual makes class abstract
};

void Character::attack(Character & victim)
{
    victim.health -= this->attackimp();
}

Character::~Character() {}

class Player : public Character
{
    public:
        Player(int x) : Character(x) {}
    private:
        virtual int attackimp() {
          return 1;
        }
};

I would have thought that if my observations were correct, then the answer to your question was "No". I dunno...

Are virtual member functions and abstract base classes really necessary to learn right now before I continue trying to set up a hierarchy? Will I be thankful to know how to use them later on in development of this program, or can I get away with not knowing them for now? I ask because I've been learning new C++ concepts for about two months now and I just want to start coding something, but it feels like every time I go to start working on something I find I need to learn x, y and z before I can. I don't mean to sound downbeat, it's just I feel I've learned a good bit and I want to put what I already know into practice and develop a really good understanding of it before moving onto learning anything new.

So to summarise, could I make a basic text based RPG with decent class hierarchical structure knowing the things I've learned so far? These are:
Classes
- Access specifiers
- Constructors(Default, Overloaded, Copy)
- Member initialisation
Inheritance
- Basic inheritance ("Is a" relationship)
- Constructing derived objects with base class constructor
- Access levels (public, protected, private)
Composition (Still getting the hang of this, but understand the concept, "Has a" relationship)
Function Parameters
- By value
- By pointer
- By reference
- By const

I might have missed one or two things but that's the gist of what I know relating to classes. Is it enough for now?
Last edited on
> Are virtual member functions and abstract base classes really necessary to learn right now
> before I continue trying to set up a hierarchy?

If you are programming with hierarchies, you would confront the need for virtual functions sooner than you anticipate right now. Abstract base classes can wait.

Since you have learnt about inheritance, you would already know that a reference (or pointer) to the derived class Player could be substituted for a reference (or pointer) to the base class Character. Once you know this much, understanding virtual functions is easy:

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
#include <iostream>

class base_class
{
    public:
        virtual ~base_class() {}
        void non_virtual_function() const  { std::cout << "base_class::non_virtual_function\n" ; }
        virtual void virtual_function() const  { std::cout << "base_class::virtual_function\n" ; }
};

struct derived_class : base_class
{
    public:
        // derived_class::non_virtual_function hides base_class::non_virtual_function
        void non_virtual_function() const
        { std::cout << "derived_class::non_virtual_function hides base_class::non_virtual_function\n" ; }

        // derived_class::non_virtual_function overrides base_class::non_virtual_function
        virtual void virtual_function() const override
        { std::cout << "derived_class::virtual_function overrides base_class::virtual_function\n\n" ; }
};

void foo( const base_class& x )
{
    // the static type (type known at compile-time) of the object referred to by 'x' is base_class
    // the dynamic type (type known only at run-time, which may be different each time
    // foo is called) of the object referred to by 'x' could be a class derived from base_class

    x.non_virtual_function() ; // not virtual; bind to the function of the static type
                               // call 'base_class::non_virtual_function
                               // the same function is called every time

    x.virtual_function() ; // virtual; bind to the function based on the the dynamic type
                           // call ????::virtual_function where ???? is the dynamic type of he object
                           // each time foo is called, this may be a different function
}

int main()
{
    derived_class derived ;
    foo(derived) ;

    struct another : base_class
    {
        void non_virtual_function() const
        { std::cout << "*** main::another::non_virtual_function ***\n" ; }

        virtual void virtual_function() const override
        { std::cout << "*** main::another::virtual_function ***\n\n" ; }
    };

    another a ;
    foo(a) ;
}

http://coliru.stacked-crooked.com/a/3a150a85a65d6279


> Will I be thankful to know how to use them later
> on in development of this program, or can I get away with not knowing them for now?

Actually this is an excellent idea. Start writing your code as if virtual functions do not exist, keep with it till you encounter a situation where you observe that run-time dispatch based of the dynamic type of the object would be extremely handy. Learn about virtual functions at that point; see what a big difference the addition of a single keyword can make; apply that knowledge to your code; and that knowledge would become internalised, intuitive. In software, as in most other endeavours, we haven't really learnt something - be it a language or a library feature - till we have used it to solve a real life problem.

So go ahead, start writing your program.
Thank you JLBorges for your detailed reply.

Since you have learnt about inheritance, you would already know that a reference (or pointer) to the derived class Player could be substituted for a reference (or pointer) to the base class Character. Once you know this much, understanding virtual functions is easy:
Isn't that polymorphism? Regardless, I'm afraid I haven't covered that yet. If that's the basic building block to understanding virtual functions then I'm goosed for the time being.

I tried to study your code to see what you are trying to explain to me, but I just can't grasp the concept at the moment. However, I really appreciate the effort you've put into trying to help me. I feel I'll have to go back to tutorials to cover this as and when I'm ready. And who knows, you may be right, it may be sooner than I had anticipated.
Topic archived. No new replies allowed.