【问题标题】:(C++) How to declare an object class member based on a conditional(C++) 如何根据条件声明对象类成员
【发布时间】:2013-12-13 16:32:25
【问题描述】:

我正在开发一个面向对象的井字游戏,但遇到了问题。我的一个类充当游戏的主控制器并控制所有其他对象。下面是该类的精简版。

最终用户可以选择一到两个玩家。因此,我没有必要同时创建第二个玩家和一个 AI 玩家。游戏只需要其中一个。正如您在下面看到的,我尝试使用 if 语句来解决问题,但对象没有范围。

如何根据传递给 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
            }
        }
};

【问题讨论】:

    标签: c++ scope


    【解决方案1】:

    我的建议是从一个公共基类(例如 Player)派生 AIPlayer 和 Player(或者更好的是 HumanPlayer),并在 Game 中有一个指向该基类的指针。然后构造函数实例化 AIPlayer 或 HumanPlayer 并将其分配给指针。

    AIPlayer 和 HumanPlayer 之间的任何不同方法都应在基类 Player 中声明为虚拟方法,然后在派生类中实现。

    【讨论】:

      【解决方案2】:

      您可能想要做的是将抽象Player 超类的指针作为游戏的成员,然后根据玩家的数量将其分配给new AIPlayernew HumanPlayer

      类似:

      #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;
                  }
              }
      };
      

      【讨论】:

        【解决方案3】:

        你应该为 Player 和 AIPlayer 创建一个(抽象的)基类,例如

        abstract class Player
        {
            virtual void MakeMove(GameBoard& b);
        }
        
        class HumanPlayer : public Player
        {...}
        
        class AIPlayer : public Player
        {...}
        

        然后在游戏中,您可以简单地存储两个 Player 实例。

        【讨论】:

          【解决方案4】:

          类继承是一个很好的解决方案。您创建一个基类并从中派生其他播放器,如下所示:

          class Player{
            virtual int move(int)=0;
          }
          
          class AIPlayer: Player{
            virtual int move(int);
          }
          
          class HumanPlayer: Player{
            virtual int move(int);
          }
          

          【讨论】:

            【解决方案5】:

            您对这里的“范围”有一点误解。类声明不是作用域——它只是支持类的数据和函数的声明。作用域存在于函数内部。

            class A
            {
                int a;
            };
            

            aA 的成员,但由于它从未被实例化,因此它不属于任何范围。

            void func()
            {
                A a;
            }
            

            func 具有定义的范围,a 是其中的一部分。当func 结束时,a 超出范围并被销毁。

            现在,回答您的问题:解决问题的最简单方法是使用继承。它将防止您必须在整个代码中进行各种条件检查以确定哪些对象是有效的,哪些是无效的。下面是一个例子:

            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;
            };
            

            在构建之后,你会以同样的方式对待它们(从Game 的角度来看,它们之间没有区别)。

            【讨论】:

            • 我喜欢这个解决方案,因为它允许每个玩家有一个唯一的名字。但是,std::unique_ptr 是 C++11 的一部分,给我的作业评分的人不允许使用 C++11 函数。有其他选择吗?
            • @navig8tr 如果允许您使用 C++03 中的 TR 功能,std::shared_ptr 也可以。或者,只要您不复制 Game 对象,您就可以使用(现已弃用)std::auto_ptr。本质上,任何智能指针包装器都可以。但是什么样的教授强迫你使用旧的语言规范?!
            • 给作业打分的TA只有Dev C++,只支持C++03。显然,他在 Dev 中运行所有程序,所以我尝试遵守这一点。我想如果我提出这个问题教授会允许的,但我没有(除了让他知道 C++ 有许多新的方便特性)。
            • 顺便说一句,我刚刚使用了类似 A.E Drew 发布的内容。我声明了 2 个 Player* 对象,然后重新分配它们:player1 = new HumanPlayerplayer2 = new AIplayer
            • @navig8tr 如果您使用原始指针,请确保正确管理内存。使用包装器的好处是它们已经为您做到了,所以当您回到石器时代时,您必须重新发明轮子。
            【解决方案6】:

            你不能做你想做的事。 您的问题的解决方案是使用继承。 创建一个没有这 2 个字段的基类,然后创建 2 个派生类,并在每个字段中放置一个。

            【讨论】:

              【解决方案7】:

              正如您所发现的,您不能使用 if 语句,因为 if 语句是在运行时执行的,但类的成员必须在编译时确定。

              这给您留下了其他几个选择:

              • 您始终可以同时声明两个人类 Player 对象和一个 AIPlayer 对象,并简单地将其中一个对象留在任何特定游戏中未使用。 (除非这样做会产生大量开销,否则我会这样做,因为这是最简单的方法)

              • 您可以声明指向所有三个对象的指针,然后使用 new 运算符动态分配您想要使用的对象。 (请务必在完成后使用 delete 取消分配它们)

              • 您可以创建 Game 的多个子类(例如 OnePlayerGame 和 TwoPlayerGame),以便子类具有您需要的额外对象(例如,Game 仅声明human1,OnePlayerGame 添加ai,TwoPlayerGame 添加human2)

              【讨论】:

              • 这些解决方案都没有比创建Player 作为基类并从中派生HumanPlayerAIPlayer 并简单地拥有一个Player* player1; Player* player2; 更简单。
              【解决方案8】:

              模板呢? 创建对象时选择类型(AIPlayer 或 Player)

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2019-10-10
                • 1970-01-01
                • 1970-01-01
                • 2012-06-22
                • 2018-06-17
                • 1970-01-01
                • 1970-01-01
                • 2010-10-25
                相关资源
                最近更新 更多