Class variables and inheritence

So I have been programming for a while but still feel like a beginner in some aspects. My question is about setting up classes and initializing values for them and their derived classes.

class Enemy
{
protected:
int health;
int attack;
int defense;
std::string name;
public:
Enemy(void);
~Enemy(void);

};

class Goblin: public Enemy
{

};

class Blob: public Enemy
{
};

class BlackKnight: public Enemy
{
};

class Dragon: public Enemy
{
};

With this example all the enemy type derived classes all have a health, attack, and defense value as well as a name. I need each to have it's own value but I'm not sure if there is a way to accomplish this here. Am I understanding how classes work incorrectly?

I know I can initialize a class in main and then set a value to health but if I do that does that mean that all the derived classes will have the same health?

Thanks for the helpahead of time.
Last edited on
First of all, try to place your code in code tags. It show up as "<>" in the Format box.

Try this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class Enemy
{
protected:
   int health;
   int attack;
   int defense;
   std::string name;

   Enemy(std::string n) : name(n) {}
public:
~Enemy(void);

};

class Goblin: public Enemy
{
public:
    Goblin : Enemy("Goblin");

};


Notice I made the Enemy constructor protected so nobody can construct an Enemy object, only objects of the subclasses. This is similar to an abstract base class, but doesn't require a pure virtual function.

All of your derived classes can pass whatever string they want to the base class constructor.

Edit: std::
Last edited on
When you instantiate an object, the object gets its own memory locations to store its member variables. A The only way that an object can change a value for all other objects of the same class, is if the member variable is static.
Last edited on
So many things you can do with classes. I don't even know all of them. Must read some more articles and tutorials!
doug4: Thanks this will help a bit so when I create an object of Goblin type I have to still set the values of health, attack, and defense in the main program? I can't set them up in the classes? And even if I could would it be considered proper practice?

I guess that's my main issue now. I see how you made it possible to set up the names for each class but how about the values for the ints for each as well?

Vlykarye: Yeah static variables would be nice from that point of view but I was trying to set up const variables for each class not have them end up all the same.

ie: goblin health = 5
blob health = 3
Hmm.. Not sure if I follow, but it seems like you are getting what you want, so that's good.

Are you trying to make a game?
Well mostly I'm using this to learn but I was making a short game out of it. Setting up a basic combat system use certain containers as well as a template object to learn how to use some of this stuff. I know how to use containers but I also need to learn how to use classes effectively with templates and struct's.

Maybe to clear up your question. I have 1 base class called enemy now every enemy has a health, defense, and attack value but each is different for each type. So I would like to set up the values in the classes and I can do that in the main function with creating an object of type goblin and setting it's values but I am trying to set it up in the class directly as it is a part of the class permanently in the project I am doing.

If you have any more thoughts they are more then welcome.
To be honest. I do not believe what doug posted is syntactically correct. I'm getting errors. What are you getting?
There should be parens and curly braces on the Goblin constructor... But that's all I see.
In addition, the Enemy destructor needs the ';' removed and '{}' added.
(At least in C::B it does)

Also, if you want each enemy type to have predetermined att/def/hp values, throw in some default values in the constructor for each enemy type
Look at this code

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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
#include <cstdlib>
#include <iostream>
#include <string>

using namespace std;

/////////////////
// Enemy Class //
/////////////////

class Enemy
{

protected:
    Enemy ( string Name );

    int health;
    int attack;
    int defense;

    string name;
    string getName ( );

public:
    ~Enemy ( );

};

Enemy::Enemy ( string Name )
{
    name = Name;
    health = 30;
    attack = 10;
    defense = 10;
}

Enemy::~Enemy ( )
{
    cout << "Enemy says:" << endl;
    cout << "My name is " << name << endl;
    cout << "My health  is " << health << endl;
    cout << "My attack  is " << attack << endl;
    cout << "My defense is " << defense << endl;
    cout << endl;
}

string Enemy::getName ( )
{
    return name;
}

//////////////////
// Goblin Class //
//////////////////

class Goblin : private Enemy
{

private:
    int health;
    int attack;
    int defense;
    // If we comment these out, the compiler looks at the base class
    // for the values that we ask for.

public:
    Goblin ( string Name );
    ~Goblin ( );

    string getName ( );
};

Goblin::Goblin ( string Name ) : Enemy ( Name )
{
}

Goblin::~Goblin ( )
{
    cout << "Goblin says:" << endl;
    cout << "My name is " << name << endl;
    cout << "My health  is " << health << endl;
    cout << "My attack  is " << attack << endl;
    cout << "My defense is " << defense << endl;
    cout << "My base health  is " << Enemy::health << endl;
    cout << "My base attack  is " << Enemy::attack << endl;
    cout << "My base defense is " << Enemy::defense << endl;
    cout << endl;

}

string Goblin::getName ( )
{
    return name;
}

////////////////
// Blob Class //
////////////////

class Blob : private Enemy
{

private:

public:

};

///////////////////////
// Other Enemy Class //
///////////////////////

class OtherEnemy
{

protected:
    int health;
    int attack;
    int defense;

    string name;

public:
    ~OtherEnemy ( );
};

OtherEnemy::~OtherEnemy ( )
{
    cout << "Other Enemy says:" << endl;
    cout << "I am the Other Enemy" << endl;
    cout << endl;
}

////////////////////////
// Other Goblin Class //
////////////////////////

class OtherGoblin : private OtherEnemy
{

public:
    OtherGoblin ( string Name, int Health = 10);
    ~OtherGoblin ( );

};

OtherGoblin::OtherGoblin ( string Name, int Health ) : OtherEnemy ( )
{
    name = Name;
    health = Health;
    attack = 20;
    defense = 5;
    // As the output shows, this is how you modify the values.
}

OtherGoblin::~OtherGoblin ( )
{
    cout << "Other Goblin says:" << endl;
    cout << "My name is " << name << endl;
    cout << "My health  is " << health << endl;
    cout << "My attack  is " << attack << endl;
    cout << "My defense is " << defense << endl;
    cout << "My base health  is " << OtherEnemy::health << endl;
    cout << "My base attack  is " << OtherEnemy::attack << endl;
    cout << "My base defense is " << OtherEnemy::defense << endl;
    cout << endl;

}

//////////////////////
// Other Blob Class //
//////////////////////

class OtherBlob : private OtherEnemy
{

private:

public:
    ~OtherBlob ( );
};

OtherBlob::~OtherBlob ( )
{
    cout << "Other Blob says:" << endl;
    cout << "I am the Other Blob" << endl;
    cout << endl;
}

////////////////////////
// Other Dragon Class //
////////////////////////

class OtherDragon : private OtherEnemy
{

private:

public:
    OtherDragon ( );
    ~OtherDragon ( );
};

OtherDragon::OtherDragon ( )
{

}

OtherDragon::~OtherDragon ( )
{
    cout << "Other Dragon says:" << endl;
    cout << "I am the Other Dragon" << endl;
    cout << endl;
}


///////////////////
// Main Function //
///////////////////

int main ( int argc, char** argv )
{
    Enemy emptyEnemy ( );
    // Remember that even if we do not declare the empty constructor

    //Enemy namedEnemy ( "Enemy" ); // Throws error.
    // We know this can't work because this constructor is protected.

    Goblin emptyGoblin ( );
    // Uses empty constructor.

    Goblin namedGoblin ( "Goblin" );
    // Uses our custom constructor.

    // You will notice that objects created with an empty constructor
    // will use an empty destructor when destroyed.

    //Blob emptyBlob; // Throws error.
    //Blob emptyBlob ( ); // Throws error.
    // We can't use either of these because the Enemy class has a custom
    // constructor. If ANY custom constructor are given, the compiler
    // will not make a default constructor for the class.

    OtherBlob emptyOtherBlob;
    OtherBlob emptyOtherBlob2 ( );

    //OtherGoblin emptyOtherGoblin; // Throws error.
    OtherGoblin emptyOtherGoblin2 ( );
    OtherGoblin namedOtherGoblin ( "Other Goblin #1" );
    OtherGoblin namedOtherGoblin2 ( "Other Goblin #2", 50 );

    // I'm still learning C++ myself, and I don't really know what happens
    // when we put () for an object verses putting none.

    OtherDragon emptyOtherDragon;
    OtherDragon emptyOtherDragon2 ( );
    // Now look at this and let it twist your mind. It's twisting mine.
    // The one without () gets the destructor called.

    // In conclusion, It seems that we do not want to use empty () after
    // the object name. These seem to use a constructor and destructor
    // that we have no control over.

    //cout << "emptyGoblin.getName() " << emptyGoblin.getName ( ) << endl;
    cout << "namedGoblin.getName() " << namedGoblin.getName ( ) << endl << endl;
    // The commented getName call is illegal. This backs up our conclusion
    // that we shouldn't use empty () objects.

    // Hopefully that clears some stuff up.

    return 0;
}


Sorry for it being so long!

Edit: added some code to help
Last edited on
So what is the answer to your question? OtherGoblin

If you want to set the variables within the base class, do it in the constructor you give. And give only one constructor. If you're worried that someone will use empty (), then tell them in a few lines of comments in your class that using empty () is wrong.

You shouldn't have to worry about setting everything yourself now. Let the derived classes handle that. Each object will have its separate values.
Yeah, my code had some syntax issues. Sorry. I was trying to get something up before leaving for home. But I hope I was able to get the idea across. I figured the code I showed would give you the pattern for adding parameters to constructor calls.

(In my defense, I considered the destructor just a declaration, with an expected .cpp file to define it, but, yes the Goblin constructor was missing the {} and had a semicolon when it shouldn't.)

I was trying to show the pattern. As far as the other variables--you can pass them in the constructor, too. so you could define the Enemy constructor as:

Enemy ( string n, int h, int a, int d ) : name(n), health(h), attack(h), defense(d) {}

This would allow each of the derived Enemy classes to define their own health, attack and defense values and assign them at object construction, and they would not have to be the same for all enemies.

1
2
Goblin::Goblin() : Enemy("Goblin", 1, 2, 3) {}
Blob::Blob() : Enemy("Blob", 4, 5, 6) {}


Of course those derived class constructor definitions have to be in the correct files.

Note: By the way, I was showing the constructors as inline just for simplicity. With inheritance, there can be some issues with inline constructors, especially if you have multiple levels of inheritance and as constructor code is less trivial. One of the standard sets supported by a static analysis tool we have will actually report a warning for an inline constructor. For production code, I would put the constructor body in a .cpp file
Hmm.. I didn't mean to put your effort down doug. The only problem I was having is:
Goblin : Enemy("Goblin"); inside the Goblin class. It kept throwing errors.

How would you fix this line? Is it just by adding () to the end of Goblin?
doug4 was just giving an example that was neither purposely giving syntactically incorrect or correct code... just a high level example of how to use inheritance for what the OP was doing.

But to answer your question it would be () after Goblin and remove ; exchange for {}
I see. Yeah. I tested it. Didn't know you could do it like that.
@Vlykarye

No offense taken. clanmjc summed it up nicely. Thanks.

Doug
Hmm. I don't see how I would take any offense.. But, alright. Lol
Thank you to you all for this valuable information this was all very useful and helped me out a lot.
Topic archived. No new replies allowed.