Help with child classes in conditional statement.

So for learning purposes and for fun I want to make a simple text based RPG.

I barely started and a wall hit me like a truck; searched all over the internet and couldn't solve my problem so I decided to post my problem here. Thank you in advance.

So there is a Player class that has 3 children: Warrior, Assassin and Mage.

I want the player to decide its class as follows:

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
	cout << "What will your character be?" << endl;
	cout << "1. Warrior" << endl;
	cout << "2. Assassin" << endl;
	cout << "3. Mage" << endl;

	int choice;
CLASS:
	choice = _getch();
	if (choice == '1')
	{
		Warrior Player;
		Player.setName(name);
		Player.setMaxRage(100);
		Player.setRage(0);
	}
	else if (choice == '2')
	{
		Assassin Player;
		Player.setName(name);
		Player.setMaxEnergy(100);
		Player.setEnergy(100);
	}
	else if (choice == '3')
	{
		Mage Player;
		Player.setName(name);
	}
	else goto CLASS;


The problem is that I can't use the objects after I created them because they're out of scope as I read. Could you please teach me what should I do? I heard I have to use pointers. I tried that somehow but it didn't work. I managed only to create Player objects (parent, not child).
closed account (48T7M4Gy)
http://www.cplusplus.com/doc/tutorial/pointers/
I don't want to be rude but that's just like saying "Google it!"... I tried a lot of things using pointers in ~2 hours... That's why I posted my problem here so I can find someone to at least try to teach me what should I do... I tried to

 
unique_ptr<Player> Player;


then inside the first if statement

 
Player.reset(new Warrior);


but the object acts like it's a Player class, not a Warrior class after the if/else...
closed account (48T7M4Gy)
http://www.learncpp.com/cpp-tutorial/73-passing-arguments-by-reference/
> but the object acts like it's a Player class, not a Warrior class after the if/else...
consider posting the code where you observe that behaviour.
consider posting the definitions of your classes.

`Player', being of type `pointer to Player' would only know about methods on the `Player' class, if you added methods in `Warrior' there is no way that `Player' could execute them directly.
However, if a method is polymorphic (virtual) then you may execute the definition that provides the child class using a pointer to base.
http://www.cplusplus.com/doc/tutorial/polymorphism/
Thanks. I'll look it up when I get home.
closed account (48T7M4Gy)
An object doesn't act like a class.

Declaring an instance of the Warrior class with the name Player is at best a misnomer.

Polymorphism and/or inheritance does not have much if anything to do with the OP scope problem.

I know. I should've changed the name into myPlayer for reading purposes
closed account (48T7M4Gy)
Way to go!
Maybe myWarrior, myAssassin etc? because they are all different objects albeit with some common attributes and methods subject to the niceties of inheritance etc

It seems to me that the issue about scope is passing those instances around and through the various methods in the game at large. Only you know. But, If that's the case then you'll find the reference material on functions and passing by reference are relevant, quicker and far more comprehensive than learning the technology in (any) forum thread.

Take it in simple steps and ask as you go. Above all write and test a few lines of code and you'll see it fall into place rapidly.
OK! Thank you everyone for making me learn more about Inheritance, Polymorphism, Pointers and Virtual Functions (oh, well... polymorphism). Now I managed to fix everything up! ^_^

Here's the entire code:

Main.cpp
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
#include <iostream>
#include <memory>
#include <conio.h>
#include "Player.h"
#include "Warrior.h"
#include "Assassin.h"
#include "Mage.h"

using namespace std;



int main()
{

	cout << "What's your name?" << endl;
	string name;
	getline(cin, name);

	system("cls");
	cout << "Hello, " << name << "." << endl;
	cout << "What will your character be?" << endl;
	cout << "1. Warrior" << endl;
	cout << "2. Assassin" << endl;
	cout << "3. Mage" << endl;

	Warrior myWarrior;
	Assassin myAssassin;
	Mage myMage;

	Player *myPlayer;

	char choice;
CLASS:
	choice = _getch();
	if (choice == '1')
	{
		myPlayer = &myWarrior;
		myPlayer->setName(name);
		myPlayer->setMaxRage(100);
		myPlayer->setRage(0);
	}
	else if (choice == '2')
	{
		myPlayer = &myAssassin;
		myPlayer->setName(name);
		myPlayer->setMaxEnergy(100);
		myPlayer->setEnergy(100);
	}
	else if (choice == '3')
	{
		myPlayer = &myMage;
		myPlayer->setName(name);
	}
	else goto CLASS;

	return 0;
}


Player.h
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
#pragma once
#include <string>

using namespace std;

class Player
{
protected:
	string name;
	string className = "Player";
	int strength = 0, agility = 0, intellect = 0;
	int attributePoints = 5;
	float health, maxHealth;
	float rage, maxRage;
	float energy, maxEnergy;
	float mana, maxMana;

public:
	Player();
	~Player();

	string getName();
	void setName(string newName);

	string getClassName();

	int getStrength();
	void setStrength(int newStrength);

	int getAgility();
	void setAgility(int newAgility);

	int getIntellect();
	void setIntellect(int newIntellect);

	int getAttributePoints();
	void setAttributePoints(int newAttributePoints);

	float getHealth();
	void setHealth(float newHealth);

	float getMaxHealth();
	void setMaxHealth(float newMaxHealth);

	virtual float getRage();
	virtual void setRage(float newRage);

	virtual float getMaxRage();
	virtual void setMaxRage(float newMaxRage);

	virtual float getEnergy();
	virtual void setEnergy(float newEnergy);

	virtual float getMaxEnergy();
	virtual void setMaxEnergy(float newMaxEnergy);

	virtual float getMana();
	virtual void setMana(float newMana);

	virtual float getMaxMana();
	virtual void setMaxMana(float newMaxMana);
};


Player.cpp
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
#include "Player.h"



Player::Player()
{
}


Player::~Player()
{
}

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

void Player::setName(string newName)
{
	name = newName;
}

string Player::getClassName()
{
	return className;
}

int Player::getStrength()
{
	return strength;
}

void Player::setStrength(int newStrength)
{
	strength = newStrength;
}

int Player::getAgility()
{
	return agility;
}

void Player::setAgility(int newAgility)
{
	agility = newAgility;
}

int Player::getIntellect()
{
	return intellect;
}

void Player::setIntellect(int newIntellect)
{
	intellect = newIntellect;
}

int Player::getAttributePoints()
{
	return attributePoints;
}

void Player::setAttributePoints(int newAttributePoints)
{
	attributePoints = newAttributePoints;
}

float Player::getHealth()
{
	return health;
}

void Player::setHealth(float newHealth)
{
	health = newHealth;
}

float Player::getMaxHealth()
{
	return maxHealth;
}

void Player::setMaxHealth(float newMaxHealth)
{
	maxHealth = newMaxHealth;
}

float Player::getRage()
{
	return 0.0f;
}

void Player::setRage(float newRage)
{
	rage = 0.0f;
}

float Player::getMaxRage()
{
	return 0.0f;
}

void Player::setMaxRage(float newMaxRage)
{
	maxRage = 0.0f;
}

float Player::getEnergy()
{
	return 0.0f;
}

void Player::setEnergy(float newEnergy)
{
	energy = 0.0f;
}

float Player::getMaxEnergy()
{
	return 0.0f;
}

void Player::setMaxEnergy(float newMaxEnergy)
{
	maxEnergy = 0.0f;
}

float Player::getMana()
{
	return 0.0f;
}

void Player::setMana(float newMana)
{
	mana = 0.0f;
}

float Player::getMaxMana()
{
	return 0.0f;
}

void Player::setMaxMana(float newMaxMana)
{
	maxMana = 0.0f;
}


Warrior.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma once
#include "Player.h"

class Warrior :
	public Player
{
public:
	Warrior();
	~Warrior();

	float getRage();
	void setRage(float newRage);

	float getMaxRage();
	void setMaxRage(float newMaxRage);
};


Warrior.cpp
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
#include "Warrior.h"



Warrior::Warrior()
{
	className = "Warrior";
}


Warrior::~Warrior()
{
}

float Warrior::getRage()
{
	return rage;
}

void Warrior::setRage(float newRage)
{
	rage = newRage;
}

float Warrior::getMaxRage()
{
	return maxRage;
}

void Warrior::setMaxRage(float newMaxRage)
{
	maxRage = newMaxRage;
}


Assassin.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma once
#include "Player.h"

class Assassin :
	public Player
{
public:
	Assassin();
	~Assassin();

	float getEnergy();
	void setEnergy(float newEnergy);

	float getMaxEnergy();
	void setMaxEnergy(float newMaxEnergy);
};


Assassin.cpp
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
#include "Assassin.h"



Assassin::Assassin()
{
	className = "Assassin";
}


Assassin::~Assassin()
{
}

float Assassin::getEnergy()
{
	return energy;
}

void Assassin::setEnergy(float newEnergy)
{
	energy = newEnergy;
}

float Assassin::getMaxEnergy()
{
	return maxEnergy;
}

void Assassin::setMaxEnergy(float newMaxEnergy)
{
	maxEnergy = newMaxEnergy;
}


Mage.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#pragma once
#include "Player.h"

class Mage :
	public Player
{
public:
	Mage();
	~Mage();

	float getMana();
	void setMana(float newMana);

	float getMaxMana();
	void setMaxMana(float newMaxMana);
};


Mage.cpp
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
#include "Mage.h"



Mage::Mage()
{
	className = "Mage";
}


Mage::~Mage()
{
}

float Mage::getMana()
{
	return mana;
}

void Mage::setMana(float newMana)
{
	mana = newMana;
}

float Mage::getMaxMana()
{
	return maxMana;
}

void Mage::setMaxMana(float newMaxMana)
{
	maxMana = newMaxMana;
}


SORRY for the big walls of text but maybe you wanted to see it! ^_^
the only little problem I have right now is that I wonder if I could get rid of the 2 other objects that I don't need anymore; for example if the player chooses to be a warrior, it'd be better if I could simply delete the myAssassin and myMage objects.
Also if there's something wrong in my code that it needs to get fixed, please tell me! I'm eager to learn!
Thank you in advance! ^_^
main.cpp
-----------

lines 27-29: No reason to declare myWarrior, myAssasin, or myMage here. Use new instead. Since myPlayer is allocated dynamically, don't forget to delete myPlayer at the end of the program to prevent a memory leak.

36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
	if (choice == '1')
	{  myPlayer = new Warrior;
            myPlayer->setName(name);
	    myPlayer->setMaxRage(100);  // See comments below about rage and maxRage.
	   myPlayer->setRage(0);
	}
	else if (choice == '2')
	{  myPlayer =  new Assassin;
		myPlayer->setName(name);
		myPlayer->setMaxEnergy(100);  // See comments below about energy and maxEnergy.
		myPlayer->setEnergy(100);
	}
	else if (choice == '3')
	{  myPlayer = new Mage;
		myPlayer->setName(name);
	}


Line 55: Get rid of the goto. gotos are evil.

player.h
---------
Line 14: If only Warriors have rage and maxRage, then rage and maxRage do not belong as members of Player.

Line 15: If only Assassins have energy and maxEnery, then enery and maxEnergy don't belong as member variables.

Line 16: If only Mage have mana, then mana and maxMana do not belong as members of Player.

Lines 45-49: getter and setters for rage and maxRage do not belong as member functions of Player.

lines 51-55: getters and setters for energy and maxEnergy don;t belong as member functions of Player.

Line 57-63: Ditto for getMana, sertMana, getMaxMana and setMaxMana.

player.cpp
----------
Line 89:107: ditto regarding getRage, setRage, getMaxRage, setMaxRage functions.
Lines 109-127: ditto regarding getEnergy, setEnergy, getMaxEnergy and setMaxEnergy functions.
Line 129-147: ditto regarding getMana/setMana, getMaxMana and setMaxMana functions.

Now you may be wondering if these functions are not members of Player, how do I access them, if I only have a pointer to a player. Good question. I would suggest adding the following accessors to your Player class:
1
2
3
4
 
  bool isWarrior () const
  { return className == "Warrior"; }
  // ditto for Assassin and Mage 

Now that we can tell the derived type of the player, we can do a static_cast
1
2
3
4
5
6
  Warrior *   warrior;
  if (myPlayer->isWarrior())
  {  warrior = static_cast<Warrior *> (myPlayer);
      //  Do warrior stuff
  }
  // Ditto for Assassin and Mage 





+1: Thanks for remembering me to delete myPlayer at the end. Also thanks for pointing out that GOTO is evil. I remember ppl saying that but I just wanted to quickly finish my job at that part. I will fix it.

-1: Now I cannot understand what you mean by myPlayer = new myWarrior. I have to write myPlayer = &myWarrior explicitely. Also I have to declare some virtual functions in Player class so the compiler knows where to look for them.

I guess I'm not that smart enough for that last part though. I couldn't understand what you tried to do. I think I have to learn more about static_cast and dynamic_cast.

EDIT: I realised I don't have to delete anything because I didn't use "new" so everything should be fine. I just created a pointer that points to an object that was already created. that specific object will get deleted automatically after the program finishes. correct me if I'm wrong.

EDIT2: Thanks everyone for helping me. I will now mark this topic as solved. I hope it will help others, too! ^_^
Last edited on
I cannot understand what you mean by myPlayer = new myWarrior.

myPlayer is an instance the base class (Player). That means we can assign a Warrior to a Player pointer. You're doing this by taking &myWarrior. I was trying to point out the preferred way of dealing with polymorphism. It's really not desirable to have separate instances of Warrior, Assassin and Mage since you have only a single player.

The point of doing myPlayer = new Warrior; or myPlayer = new Assassin; or myPlayer = new Mage; is to assign an instance of the proper derived class to myPlayer.

The second thing I was trying to point out is that attributes of derived classes (e.g Warrior has rage, Mage has mana, etc) belong in the derived class, not in the base class. Yes, it works the way you have it, but again that's not the proper way to deal with polymorphism.

If we move rage into the Warrior class as I suggested, then we can't access it through the base class. In order to access rage, we need a pointer to a Warrior. In the snippet I showed above, we check myPlayer->isWarrior(). That tells us that myPlayer in in fact a pointer to a Warrior instance. Therefore we can safely cast myPlayer to a Warrior pointer. Now with a pointer to a Warrior, we can do stuff specific to a Warrior. The same goes for Assassin and Mage.
Last edited on
closed account (48T7M4Gy)
The point of doing myPlayer = new Warrior; or myPlayer = new Assassin; or myPlayer = new Mage; is to assign an instance of the proper derived class to myPlayer.


Maybe the difficulty Chipp is having with this is the choice of the word 'player' vs the type of player. Maybe the Player class could be renamed Role and a human player called myPlayer can be seen to take on the role of Warrior etc via Abstraction's code snippet quoted.
Topic archived. No new replies allowed.