Jump to content
  • Advertisement
Sign in to follow this  
Assassin7257

Polymorphism and Inheritance

This topic is 2476 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts


#include<iostream>
#include<string>
#include<ctime>
#include<cstdlib>
using namespace std;

class Player
{
protected:
string* m_name; //players name
int m_health; //the players health

public:
virtual int getHealth(); //returns the players health
virtual int attack();
string getName(); //returns the users name

Player(const string& name = " ", int health = 100); //constructor sets users name
Player(const Player& aPlayer); //copy constructor
~Player();
};

Player::Player(const string& name, int health)
{
m_name = new string(name);
m_health = health;
}

Player::~Player()
{
delete m_name;
}

Player::Player(const Player& aPlayer)
{
m_name = new string(*(aPlayer.m_name));
m_health = aPlayer.m_health;
}

int Player::getHealth()
{
return m_health;
}

string Player::getName()
{
return *m_name;
}

int Player::attack()
{
cout << "NOthing " << endl;
int attack = 15;
return attack;
}

class Warrior : public Player
{
public:
int getHealth(); //returns warriors health
int attack(); //the user attacks

Warrior(string name = " ", int health = 100);
};

Warrior::Warrior(string name, int health):
Player(name, health)
{}

int Warrior::getHealth()
{
return m_health;
}

int Warrior::attack()
{
srand(time(0));

int choice;
cout << "Pick an attack: " << endl;
cout << "1 - Lunge" << endl;
cout << "2 - Slash" << endl;
cout << "3 - Stab" << endl;
cin >> choice;

switch(choice)
{
case 1:
{
int attack = ( (rand() % 20) + 1);
return attack;
}

case 2:
{
int attack = ( (rand() % 25) + 1);
return attack;
}

case 3:
{
int attack = ( (rand() % 15) + 1);
return attack;
}

default:
cout << "You choose an illegal move." << endl;
}
}

class Mage : public Player
{
public:
int attack(); //different attack for a mage

Mage(string name = " ", int health = 100);
};

Mage::Mage(string name, int health):
Player(name, health)
{}

int Mage::attack()
{
srand(time(0));

int choice;

cout << "Select an attack: " << endl;
cout << "1 - Fire Blast" << endl;
cout << "2 - Ice Strike" << endl;
cout << "3 - Ground Strike" << endl;
cin >> choice;

switch(choice)
{
case 1:
{
int attack = ( (rand () % 30) + 1);
return attack;
}

case 2:
{
int attack = ( (rand() % 25) + 1);
return attack;
}

case 3:
{
int attack = ( (rand() % 35) + 1);
return attack;
}
}
}

class Archer : public Player
{
public:
int potion(); //add potion if user request
int attack(); //attacks

Archer(string name = " ", int health = 100);
};

Archer::Archer(string name, int health):
Player(name, health)
{}

int Archer::attack()
{
srand(time(0));

int choice;

cout << "Select an attack: " << endl;
cout << "1 - Short Range" << endl;
cout << "2 - Long Range" << endl;
cout << "3 - Potion" << endl;
cin >> choice;

switch(choice)
{
case 1:
{
int attack = ( (rand() % 20) + 1);
return attack;
}

case 2:
{
int attack = ( (rand() % 25) + 1);
return attack;
}

case 3:
{
int getPotion = potion();
return getPotion;
}
}
}

int Archer::potion()
{
int potion = 15;
return potion;
}

class enemy
{
private:
enemy* pNext; //gets the memory address of the next enemy
string m_name; //enemies name
int m_health; //the enemies health

public:
enemy* getNext() const; //returns the next pointer
void setNext(enemy* next); //sets the next enemy on the list
string getName() const; //returns enemies name
int getHealth(); //returns health
int attack(); //the computer attacks

enemy(string name = " ", int health = 100);
};

enemy::enemy(string name, int health):
m_name(name),
m_health(health),
pNext(0)
{}

void enemy::setNext(enemy* next)
{
pNext = next;
}

enemy* enemy::getNext() const
{
return pNext;
}


string enemy::getName() const
{
return m_name;
}

int enemy::getHealth()
{
m_health += 100;
return m_health;
}

int enemy::attack()
{
srand(time(0));

int attack = ( (rand() % 25) + 1);
return attack;
}

class fightingGround
{

friend ostream& operator<<(ostream& os, const fightingGround& aFightingGround);

private:
enemy* m_pHead; //head of the chain

public:
void AddEnemies(); //adds enemies
void DeleteEnemies(); //deletes enemies when done fighting
void Clear(); //clears enemies
int Attack(); //attacks the player the top user
enemy* getHead(); //returns top pointer


fightingGround();
~fightingGround();
};

fightingGround::fightingGround():
m_pHead(0)
{}

fightingGround::~fightingGround()
{
Clear();
}

enemy* fightingGround::getHead()
{
return m_pHead;
}

int fightingGround::Attack()
{
enemy* fightEnemy = m_pHead;

srand(time(0));
int attack = ( (rand() % 25) + 1);
return attack;
}

void fightingGround::AddEnemies()
{
cout << "Type in the name of an enemy: " << endl;
string name;
cin >> name;

enemy* pNewEnemy = new enemy(name);

if(m_pHead == 0)
{
m_pHead = pNewEnemy;
}

else
{
enemy* pIter = m_pHead;
while(pIter->getNext() != 0)
{
pIter = pIter->getNext();
}
pIter->setNext(pNewEnemy);
}

}


void fightingGround::DeleteEnemies()
{
if(m_pHead == 0)
{
cout << "NO MORE ENEMIES" << endl;
}
else
{
enemy* pTemp = m_pHead;
m_pHead = m_pHead->getNext();
delete pTemp;
}
}

void fightingGround::Clear()
{
while(m_pHead != 0)
{
DeleteEnemies();
}

}

ostream& operator<<(ostream& os, const fightingGround& afightingGround)
{
enemy* pIter = afightingGround.m_pHead;

os << "\nHere is a list of the enemies :\n";
if(pIter == 0)
{
os << "There are no more enemies.\n";
}

else
{
while(pIter != 0)
{
os << pIter->getName() << endl;
pIter = pIter->getNext();
}
}

return os;
}

void fight(Player* aPlayer, fightingGround aFightingGround)
{
cout << "You've decided to fight, well this task isn't easy." << endl;

enemy* enemyFight = aFightingGround.getHead();

int playerHealth = aPlayer->getHealth();
int computerHealth = enemyFight->getHealth();

bool loop = true;

while(loop)
{
int attack = aPlayer->attack();
cout << "You hit a " << attack << "\n";
int computerHealth = computerHealth - attack;
cout << "The computer has " << computerHealth << "\n";

int computerAttack = enemyFight->attack();
cout << "The computer hit " << computerAttack << "\n";
playerHealth = playerHealth - computerAttack;
cout << "You have " << playerHealth << "\n";

if(playerHealth <= 0)
{
cout << "The computer has defeated you.." << endl;
loop = false;
}

if(computerHealth <= 0)
{
cout << "You've beaten the computer !" << endl;
aFightingGround.DeleteEnemies();
loop = false;
}
}

delete enemyFight;
enemyFight = 0;
delete aPlayer;
aPlayer = 0;
}


int main()
{
int classChoice;
Player* pPlayerClass;

fightingGround theFightingGround;

theFightingGround.AddEnemies();



cout << "Select a Class: " << endl;
cout << "1 - Warrior" << endl;
cout << "2 - Mage" << endl;
cout << "3 - Archer" << endl;
cin >> classChoice;

switch(classChoice)
{
case 1:
{
cout << "You choose a Warrior." << endl;
cout << "What is his name: " << endl;
string name;
cin >> name;
pPlayerClass = new Warrior(name);
break;
}

case 2:
{
cout << "You choose a Mage." << endl;
cout << "What is his name: " << endl;
string name;
cin >> name;
pPlayerClass = new Mage(name);
break;
}

case 3:
{
cout << "You choose a Warrior." << endl;
cout << "What is his name: " << endl;
string name;
cin >> name;
pPlayerClass = new Archer(name);
break;
}
}

fight(pPlayerClass, theFightingGround);
cout << "Welcome BACK !" << endl;

return 0;
}


So I'm learning to use polymorphism and inheritance, so this is a game I tried to make but everytime at the end of it, it keeps crashing. I'm guessing there's a problem with the deconstructor. Any Ideas ?

Share this post


Link to post
Share on other sites
Advertisement
There is a few issues here

First, you redeclare the enemyHealth inside the game loop. You don't want to do that, you want to use the enemyHealth that is already declared before the loop.

The next thing to note here is that you pass the fightingGround by value to the fight function, so the fight function gets a copy of the fightingGround.
Its important to understand that this copy contains pointers internally that still points to the same objects as the original fightingGround (the one created in main)

And then at the end of the fight function you delete enemyFight, which is a pointer to the head of the list (both the original and the copy of fightingGround is affected by this!)

I changed the code to pass the fightingGround by reference to the fight function so that no copy exists.
I also removed int from the enemyHealth inside the game loop since redeclaring, and thereby hiding, the one declared earlier is not what we want.

Finally, I moved the deletion of the player and the enemies to the end of main, which is the function that created them. This is probably not necessary but it makes the code path clearer

Here is the result

#include<iostream>
#include<string>
#include<ctime>
#include<cstdlib>
using namespace std;

class Player
{
protected:
string* m_name; //players name
int m_health; //the players health

public:
virtual int getHealth(); //returns the players health
virtual int attack();
string getName(); //returns the users name

Player(const string& name = " ", int health = 100); //constructor sets users name
Player(const Player& aPlayer); //copy constructor
~Player();
};

Player::Player(const string& name, int health)
{
m_name = new string(name);
m_health = health;
}

Player::~Player()
{
delete m_name;
}

Player::Player(const Player& aPlayer)
{
m_name = new string(*(aPlayer.m_name));
m_health = aPlayer.m_health;
}

int Player::getHealth()
{
return m_health;
}

string Player::getName()
{
return *m_name;
}

int Player::attack()
{
cout << "NOthing " << endl;
int attack = 15;
return attack;
}

class Warrior : public Player
{
public:
int getHealth(); //returns warriors health
int attack(); //the user attacks

Warrior(string name = " ", int health = 100);
};

Warrior::Warrior(string name, int health):
Player(name, health)
{}

int Warrior::getHealth()
{
return m_health;
}

int Warrior::attack()
{
srand(time(0));

int choice;
cout << "Pick an attack: " << endl;
cout << "1 - Lunge" << endl;
cout << "2 - Slash" << endl;
cout << "3 - Stab" << endl;
cin >> choice;

switch(choice)
{
case 1:
{
int attack = ( (rand() % 20) + 1);
return attack;
}

case 2:
{
int attack = ( (rand() % 25) + 1);
return attack;
}

case 3:
{
int attack = ( (rand() % 15) + 1);
return attack;
}

default:
cout << "You choose an illegal move." << endl;
}
}

class Mage : public Player
{
public:
int attack(); //different attack for a mage

Mage(string name = " ", int health = 100);
};

Mage::Mage(string name, int health):
Player(name, health)
{}

int Mage::attack()
{
srand(time(0));

int choice;

cout << "Select an attack: " << endl;
cout << "1 - Fire Blast" << endl;
cout << "2 - Ice Strike" << endl;
cout << "3 - Ground Strike" << endl;
cin >> choice;

switch(choice)
{
case 1:
{
int attack = ( (rand () % 30) + 1);
return attack;
}

case 2:
{
int attack = ( (rand() % 25) + 1);
return attack;
}

case 3:
{
int attack = ( (rand() % 35) + 1);
return attack;
}
}
}

class Archer : public Player
{
public:
int potion(); //add potion if user request
int attack(); //attacks

Archer(string name = " ", int health = 100);
};

Archer::Archer(string name, int health):
Player(name, health)
{}

int Archer::attack()
{
srand(time(0));

int choice;

cout << "Select an attack: " << endl;
cout << "1 - Short Range" << endl;
cout << "2 - Long Range" << endl;
cout << "3 - Potion" << endl;
cin >> choice;

switch(choice)
{
case 1:
{
int attack = ( (rand() % 20) + 1);
return attack;
}

case 2:
{
int attack = ( (rand() % 25) + 1);
return attack;
}

case 3:
{
int getPotion = potion();
return getPotion;
}
}
}

int Archer::potion()
{
int potion = 15;
return potion;
}

class enemy
{
private:
enemy* pNext; //gets the memory address of the next enemy
string m_name; //enemies name
int m_health; //the enemies health

public:
enemy* getNext() const; //returns the next pointer
void setNext(enemy* next); //sets the next enemy on the list
string getName() const; //returns enemies name
int getHealth(); //returns health
int attack(); //the computer attacks

enemy(string name = " ", int health = 100);
};

enemy::enemy(string name, int health):
m_name(name),
m_health(health),
pNext(0)
{}

void enemy::setNext(enemy* next)
{
pNext = next;
}

enemy* enemy::getNext() const
{
return pNext;
}


string enemy::getName() const
{
return m_name;
}

int enemy::getHealth()
{
m_health += 100;
return m_health;
}

int enemy::attack()
{
srand(time(0));

int attack = ( (rand() % 25) + 1);
return attack;
}

class fightingGround
{

friend ostream& operator<<(ostream& os, const fightingGround& aFightingGround);

private:
enemy* m_pHead; //head of the chain

public:
void AddEnemies(); //adds enemies
void DeleteEnemies(); //deletes enemies when done fighting
void Clear(); //clears enemies
int Attack(); //attacks the player the top user
enemy* getHead(); //returns top pointer


fightingGround();
~fightingGround();
};

fightingGround::fightingGround():
m_pHead(0)
{}

fightingGround::~fightingGround()
{
Clear();
}

enemy* fightingGround::getHead()
{
return m_pHead;
}

int fightingGround::Attack()
{
enemy* fightEnemy = m_pHead;

srand(time(0));
int attack = ( (rand() % 25) + 1);
return attack;
}

void fightingGround::AddEnemies()
{
cout << "Type in the name of an enemy: " << endl;
string name;
cin >> name;

enemy* pNewEnemy = new enemy(name);

if(m_pHead == 0)
{
m_pHead = pNewEnemy;
}

else
{
enemy* pIter = m_pHead;
while(pIter->getNext() != 0)
{
pIter = pIter->getNext();
}
pIter->setNext(pNewEnemy);
}

}


void fightingGround::DeleteEnemies()
{
if(m_pHead == 0)
{
cout << "NO MORE ENEMIES" << endl;
}
else
{
enemy* pTemp = m_pHead;
m_pHead = m_pHead->getNext();
delete pTemp;
}
}

void fightingGround::Clear()
{
while(m_pHead != 0)
{
DeleteEnemies();
}

}

ostream& operator<<(ostream& os, const fightingGround& afightingGround)
{
enemy* pIter = afightingGround.m_pHead;

os << "\nHere is a list of the enemies :\n";
if(pIter == 0)
{
os << "There are no more enemies.\n";
}

else
{
while(pIter != 0)
{
os << pIter->getName() << endl;
pIter = pIter->getNext();
}
}

return os;
}

void fight(Player* aPlayer, fightingGround& aFightingGround)
{
cout << "You've decided to fight, well this task isn't easy." << endl;

enemy* enemyFight = aFightingGround.getHead();

int playerHealth = aPlayer->getHealth();
int computerHealth = enemyFight->getHealth();

bool loop = true;

while(loop)
{
int attack = aPlayer->attack();
cout << "You hit a " << attack << "\n";
//int computerHealth = computerHealth - attack;
computerHealth = computerHealth - attack;
cout << "The computer has " << computerHealth << "\n";

int computerAttack = enemyFight->attack();
cout << "The computer hit " << computerAttack << "\n";
playerHealth = playerHealth - computerAttack;
cout << "You have " << playerHealth << "\n";

if(playerHealth <= 0)
{
cout << "The computer has defeated you.." << endl;
loop = false;
}

if(computerHealth <= 0)
{
cout << "You've beaten the computer !" << endl;
loop = false;
}
}

//delete enemyFight;
//enemyFight = 0;
//delete aPlayer;
//aPlayer = 0;
}


int main()
{
int classChoice;
Player* pPlayerClass;

fightingGround theFightingGround;

theFightingGround.AddEnemies();



cout << "Select a Class: " << endl;
cout << "1 - Warrior" << endl;
cout << "2 - Mage" << endl;
cout << "3 - Archer" << endl;
cin >> classChoice;

switch(classChoice)
{
case 1:
{
cout << "You choose a Warrior." << endl;
cout << "What is his name: " << endl;
string name;
cin >> name;
pPlayerClass = new Warrior(name);
break;
}

case 2:
{
cout << "You choose a Mage." << endl;
cout << "What is his name: " << endl;
string name;
cin >> name;
pPlayerClass = new Mage(name);
break;
}

case 3:
{
cout << "You choose a Warrior." << endl;
cout << "What is his name: " << endl;
string name;
cin >> name;
pPlayerClass = new Archer(name);
break;
}
}

fight(pPlayerClass, theFightingGround);
cout << "Welcome BACK !" << endl;

delete pPlayerClass;
theFightingGround.DeleteEnemies();

return 0;
}


The problem here is that the copy of fightingGround that fight receives will have its destructor called when fight returns (this deletes all the objects inside the fightingGround for both the copy and the original)
The thing is that only the copy of fightingGround will have its m_pHead set to zero, so the fightingGround object that still exists in main has had all its objects removed under its feet without having its m_pHead pointer set to zero.
This causes havoc when its destructor is called at program return.
Passing the fightingGround by reference fixes this since there is no longer a copy around to mess things up with the m_pHead member.

Basically, the way fightingGround is designed and implemented, you better not allow copies of it to exists. In C++ this is typically achieved by giving the class a private copy constructor and a private assignment operator.

Share this post


Link to post
Share on other sites
Your player class needs a virtual destructor, otherwise C++ will only call the destructor of the declared type of the pointer it is deleting.

For example:


#include<iostream>

class Parent
{
public:
/* virtual */ ~Parent()
{
std::cout << "Destroying Parent\n";
}
};

class Middle : public Parent
{
public:
~Middle()
{
std::cout << "Destroying Middle\n";
}
};

class Child : public Middle
{
public:
~Child()
{
std::cout << "Destroying Child\n";
}
};

int main()
{
std::cout << "Declared type Parent:\n";
Parent *parent = new Child();
delete parent;

std::cout << "---------------------\n";

std::cout << "Declared type Middle:\n";
Middle *middle= new Child();
delete middle;

std::cout << "---------------------\n";

std::cout << "Declared type Child:\n";
Child *child= new Child();
delete child;
}


Try running the above program with the "virtual" keyword included and excluded and compare the output and make sure you understand what is happening. The rule of thumb is that destructors in classes with any virtual functions need to be virtual. You should add an empty virtual destructor to such classes if they lack one.

I don't think this is causing your error. I believe the problem is twofold. First, you are passing the fightingGround instance by value, and it it does not handle copying or assignment (that is, it does not obey the Rule of Three. This causes the list to be deleted twice. Secondly, you delete "enemyFight" - which is also in the fightingGround list and thus will be deleted twice (three times in theory).

There are a number of solutions. Implementing copying is complex - so I would advise against that. Instead, either pass the fightingGround instance by reference into the function, or actually don't pass it at all and declare it inside the fight() function. The second is to say that the fightingGround instance owns all the enemies it has - so the fight() function should not call delete on the enemy that was retrieved from it.

Finally, your code should be more robust in the face of errors. In main(), your Player pointer is uninitialsed. If the user enters non-numeric data, or a number that isn't 1, 2 or 3, then the code will access this uninitialised pointer. You should certainly initialise the pointer to 0 (or nullptr if your compiler supports it). You should then add in error handling such that the fight() function is not called until the pointer points to a valid Player instance. For instance, you could loop until the player enters a number that is in the correct range.

Finally, some stylistic suggestions. I would probably move the delete call on the player instance into main() - as this is where the variable is created. I would pass the Player instance by reference to fight().

Share this post


Link to post
Share on other sites
You are not following the rule of three, and I haven't looked at your code in detail, but that often results in problems like a pointer being deleted twice.

You are complicating things needlessly by making m_name a pointer to string: Make it a string instead, and you'll be a lot less likely to mess things up.

fight shouldn't delete the pointer that it gets passed: It's much easier to keep track of things if the same part of the code that allocates also releases. It's even easier to get things right if you use shared_ptr so delete gets called automatically when nobody is using the object.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!