Reputation: 1844
I'm working on an object-oriented tic tac toe game and I'm having a problem. One of my classes acts as the main controller of the game and controls all other objects. Below is a stripped back version of the class.
The end user can choose between one and two players. So, it is unnecessary for me to create both a second player AND an ai player. The game only needs one or the other. As you can see below, I tried to use an if statement to solve the problem, but the objects do not have scope.
How can can I initialize one object or the other based on the number of players passed to the Game constructor?
Thanks!
Game.h
#include "Player.h"
#include "AIPlayer.h"
class Game
{
private:
Player human;
// I would like to put these here so they have scope
// but it is unecessary to declare them both
// If the user chooses one player then human2 is unecessary
// if the user choosed two player then ai is unecessary
AIPlayer ai;
Player human2;
public:
Game(int players)
{
if (players == 1)
{
AIPlayer ai; // this does not have scope
}
else
{
Player human2; // this does not have scope
}
}
};
Upvotes: 0
Views: 772
Reputation: 15872
You have a slight misunderstanding of "scope" here. A class declaration is not a scope - it is just a declaration of the data and functions that support the class. Scope exists inside the functions.
class A
{
int a;
};
a
is a member of A
, but since it has never been instantiated, it is not part of any scope.
void func()
{
A a;
}
func
has a defined scope and a
is a part of it. When func
ends, a
goes out of scope and is destroyed.
Now, to your question: The simplest way to solve your problem is to use inheritance. It will prevent you from having to do all sorts of conditional checks throughout your code to determine which objects are valid and which are not. An example is below:
class Player
{
public:
virtual void Move() = 0; // pure virutal function
};
class HumanPlayer()
{
public:
...
virtual void Move() { ... }
};
class AIPlayer()
{
public:
...
virtual void Move() { ... }
};
class Game
{
public:
Game(unsigned int humanPlayerCount = 0)
{
if (humanPlayerCount == 0)
{
m_player1.reset(new AIPlayer());
m_player2.reset(new AIPlayer());
}
else if (humanPlayerCount == 1)
{
m_player1.reset(new HumanPlayer());
m_player2.reset(new AIPlayer());
}
else // assume it is 2
{
m_player1.reset(new HumanPlayer());
m_player2.reset(new HumanPlayer());
}
}
...
private:
std::unique_ptr<Player> m_player1;
std::unique_ptr<Player> m_player2;
};
After the construction, you would treat them the same (there would be no difference between them from the Game
's point of view).
Upvotes: 1
Reputation: 91
What about a template? choose the type (AIPlayer or Player) when creating the object
Upvotes: 0
Reputation: 433
Class inheritance is a good solution. You create a base class and derive the other players from it, like so:
class Player{
virtual int move(int)=0;
}
class AIPlayer: Player{
virtual int move(int);
}
class HumanPlayer: Player{
virtual int move(int);
}
Upvotes: 1
Reputation: 73071
As you discovered, you can't use an if statement, since if statements are executed at run-time, but the members of the class have to be determined at compile time.
That leaves you several other options:
You could always declare both two human Player objects and an AIPlayer object, and simply leave one of the objects unused in any particular game. (That's what I would do unless there was a tremendous amount of overhead incurred by doing so, as it's the simplest way)
You could declare pointers to all three objects, and then use the new operator to dynamically allocate just the ones you want to use. (Be sure to de-allocate them with delete when you are done with them)
You could make several subclasses of Game, (e.g. OnePlayerGame and TwoPlayerGame), such that the subclass has the extra object you need (e.g. Game declares only human1, OnePlayerGame adds ai, and TwoPlayerGame adds human2)
Upvotes: 0
Reputation: 1195
You cannot do what you want. The solution to your problem is to use inheritance. Make a base class without any of those 2 fields and then create 2 derived classes and in each put one of the fields.
Upvotes: 0
Reputation: 7399
You should make an (abstract) base class for both Player and AIPlayer, e.g.
abstract class Player
{
virtual void MakeMove(GameBoard& b);
}
class HumanPlayer : public Player
{...}
class AIPlayer : public Player
{...}
and then in game, you can simply store the two Player instances.
Upvotes: 1
Reputation: 6846
My suggestion would be to derive both AIPlayer and Player (or perhaps better, HumanPlayer) from a common base class (e.g., Player), and have a pointer to that base class in Game. The constructor then instantiates either AIPlayer or HumanPlayer and assigns it to the pointer.
Any methods that differ between AIPlayer and HumanPlayer should be declared in the base class Player as virtual methods, and then be implemented in the derived classes.
Upvotes: 6
Reputation: 2137
What you may want to do is have a pointer to a abstract Player
superclass as a member of game and either assign it to a new AIPlayer
or a new HumanPlayer
depending on the number of players.
Something like:
#include "Player.h"
#include "AIPlayer.h"
#include "HumanPlayer.h"
class Game
{
private:
Player* other_player;
public:
Game(int players)
{
if (players == 1)
{
other_player = new AIPlayer;
}
else
{
other_player = new HumanPlayer;
}
}
};
Upvotes: 1