navig8tr
navig8tr

Reputation: 1844

(C++) How to declare an object class member based on a conditional

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

Answers (8)

Zac Howland
Zac Howland

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

Glandy
Glandy

Reputation: 91

What about a template? choose the type (AIPlayer or Player) when creating the object

Upvotes: 0

Ragnar
Ragnar

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

Jeremy Friesner
Jeremy Friesner

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

Matei Florescu
Matei Florescu

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

Wilbert
Wilbert

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

zennehoy
zennehoy

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

A.E. Drew
A.E. Drew

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

Related Questions