C++ 在C++;

C++ 在C++;,c++,oop,data-transfer,data-exchange,C++,Oop,Data Transfer,Data Exchange,我被分配到大学工作。我的任务是写一个蛇类游戏,一般都会玩得很开心,然后把它变成我自己的。除了必须用面向对象的方法编写之外,没有给我其他指令。我对C++非常熟悉,但我缺乏一些真实的体验。无论如何。我需要一个对象来访问其他对象的一些数据成员。例如,我有一个蛇对象,我需要它能够访问游戏中“水果”对象的坐标,以便蛇可以吃水果并生长。我知道有很多方法可以解决这个问题。我目前在代码中使用的一种方法是通过指向构造函数的指针传递成员。然而,我不喜欢这种方法,因为代码很快就会变得混乱,很难在一段时间后知道是什么。

我被分配到大学工作。我的任务是写一个蛇类游戏,一般都会玩得很开心,然后把它变成我自己的。除了必须用面向对象的方法编写之外,没有给我其他指令。我对C++非常熟悉,但我缺乏一些真实的体验。无论如何。我需要一个对象来访问其他对象的一些数据成员。例如,我有一个蛇对象,我需要它能够访问游戏中“水果”对象的坐标,以便蛇可以吃水果并生长。我知道有很多方法可以解决这个问题。我目前在代码中使用的一种方法是通过指向构造函数的指针传递成员。然而,我不喜欢这种方法,因为代码很快就会变得混乱,很难在一段时间后知道是什么。简单的解决方法是只使用全局变量,但是我不想再使用这种方法,因为我们正在处理一个大学项目,一般来说,我听说全局变量是不可行的。将所有代码放在一个大类中也会起作用,但这会使代码更加混乱,并否定了首先使用oop的全部原因。我知道继承是一件事,但是从我的理解来看,一旦您创建了从包含所有数据的某个基类派生的不同对象,成员就不会受到任何影响,并且可以具有不同的值。我读过一些关于数据传输对象的文章,但显然它们也不是一个好主意


好的,总而言之,我可以让我的代码工作,但我知道这可能不是最好的方法,我愿意学习如何做得更好或更好。欢迎提出任何建议或想法。我想知道这个行业是怎么做的;)。提前感谢您的帮助。

您通常不会像那样公开数据成员,而是提供对封装它们的对象的访问,使用允许读取(或可能写入)信息的函数

在面向对象编程的上下文中,您会遇到“getter”和“setter”,但是这些访问器不需要是“瘦的”:它们应该反映类的逻辑属性,这些属性可能直接映射到单个数据成员,也可能不直接映射到单个数据成员

因此:

当然,现在我们仍然有同样的问题:如果我添加另一个类,它如何“看到”水果以使用其闪亮的新访问器函数

class Fruit
{
public:
   Flavour GetFlavour() const;
   void SetFlavour(const Flavour newFlavour);

private:
   Flavour m_flavour;
};

class Juicer
{
public:
   void UseFoodItem(const Fruit&);
};

int main()
{
   Fruit fruit;
   fruit.GetFlavour();
   
   Juicer juicer;
   juicer.useFoodItem(fruit);
}
看看我是如何在需要的时候将对水果的引用传递到榨汁机的?这很干净。它并不凌乱,因为你不必担心谁拥有什么以及拥有多长时间。但是你只能使用里面的水果
UseFoodItem
。如果您需要更持久的引用,以便其他函数也可以使用它,该怎么办

事实证明,引用仍然是一种很好的方法。让我们让示例更接近您的用例:

// Let's imagine you've defined this somewhere, and it contains
// both X and Y coordinates on a shared grid
class Location;

class Fruit
{
public:
   Location GetLocation() const;
   void MoveTo(const Location newLocation);

private:
   Location m_location;
};

class Snake
{
public:
   Snake(const Fruit& fruit) : m_fruit(fruit) {}
   void DoAThing();

private:
   const Fruit& m_fruit;
};

int main()
{
   Fruit fruit;
   fruit.MoveTo(Location{20, 15});
   
   Snake snake(fruit);
   snake.DoAThing();
}
(您是否注意到位置设置器不仅仅是“SetLocation”?从逻辑上讲,对水果执行的操作是移动它。这是通过更改其“位置”来实现的,这是一个实现细节,因此我们将其隐藏在类的接口后面。)

在这里,
DoAThing
是你的蛇在移动,还是你游戏中发生的任何事情。关键是,您可以从其内部以及您添加的任何其他功能访问
水果。只要
main
中的
水果保持在范围内,这是安全的

使用智能指针可以使其更加安全。
shared\u ptr
可以在许多其他对象之间共享,而不会造成太多混乱:在游戏中没有任何其他对象需要它之前,你会知道水果是存在的

尽管如此,我还是不想让蛇知道关于水果的任何事情。我会把这个逻辑放到别的地方,在更高的层次上。我希望你的蛇由某个函数/类控制,你的果实归该类所有,在每一步它都应该检查蛇和果实,然后决定如果检测到碰撞怎么办。现在这变得有点复杂了,但它可能看起来大致如下:

// Let's imagine you've defined this somewhere, and it contains
// both X and Y coordinates on a shared grid
class Location;

// Let's imagine you've defined this somewhere, and it represents
// an action a user can take. For example, "move the snake up" (which
// would likely be triggered by an "up arrow" keypress).

// Let's imagine you've defined this somewhere, and it blocks for
// user input then transforms that into an Action
Action AcceptUserInput();

class Fruit
{
public:
   Location GetLocation() const;
   void MoveTo(const Location newLocation);

private:
   Location m_location;
};

class Snake
{
public:
   Location GetLocation() const;
   void MoveTo(const Location newLocation);

private:
   Location m_location;
};

class Game
{
public:
   void SetSnakeInitialLocation(const Location snakeLocation)
   {
      m_snake.MoveTo(snakeLocation);
   }
   
   void AddFruit(const Location fruitLocation)
   {
      Fruit newFruit;
      newFruit.MoveTo(fruitLocation);
      
      m_fruits.push_back(newFruit);
   }
   
   void PerformAction(const Action action)
   {
      // If the action is to move the snake, do that by
      // calling things on m_snake, e.g. m_snake.MoveTo(newLocation)
      // Otherwise, do whatever needed.
      
      // Now, let's examine the state of the board. We have
      // all the information needed to do that, and don't have
      // to obtain it from anywhere else.
      for (Fruit& fruit : m_fruits)
      {
          if (m_snake.Intersects(fruit))
          {
             // Grow the snake; remove the fruit
             // 
             // (N.B. you can't use ranged-for like this if you're
             // going to erase from m_fruits, but that detail is out
             // of scope of this answer)
          }
      }
   }

private:
   std::vector<Fruit> m_fruits;
   Snake m_snake;
};

int main()
{
   Game game;
   game.AddFruit(Location{20, 15});
   game.SetSnakeInitialLocation(Location{0, 0});
   
   while (true)
   {
      Action nextAction = AcceptUserInput();
      game.PerformAction(nextAction);
   }
}
//假设您在某个地方定义了它,它包含
//共享栅格上的X和Y坐标
班级位置;
//让我们想象一下你在某处定义了它,它代表了
//用户可以采取的操作。例如,“向上移动蛇”(哪一个
//可能由“向上箭头”键触发)。
//让我们想象一下,你在某个地方定义了它,它会为
//然后,用户输入将其转换为操作
操作AcceptUserInput();
等级水果
{
公众:
Location GetLocation()常量;
void MoveTo(const Location newLocation);
私人:
位置m_位置;
};
蛇类
{
公众:
Location GetLocation()常量;
void MoveTo(const Location newLocation);
私人:
位置m_位置;
};
班级游戏
{
公众:
void setsnakeinitialocation(常量位置蛇位)
{
m_snake.MoveTo(蛇定位);
}
无效添加水果(常量位置水果位置)
{
新果;
新水果。移动到(水果位置);
把水果推回去(新水果);
}
无效执行(持续行动)
{
//如果动作是移动蛇,则通过
//在m_snake上调用东西,例如m_snake.MoveTo(新位置)
//否则,做任何需要的事情。
//现在,让我们检查一下董事会的状态。我们有
//所有的信息都需要这样做,而且没有
//从其他任何地方获得它。
适用于(水果和水果:m_水果)
{
if(m_蛇相交(果))
{
//把蛇养大;把水果移走
// 
//(注意:如果你是
//将要从m_fruits中删除,但该细节已被删除
//
// Let's imagine you've defined this somewhere, and it contains
// both X and Y coordinates on a shared grid
class Location;

// Let's imagine you've defined this somewhere, and it represents
// an action a user can take. For example, "move the snake up" (which
// would likely be triggered by an "up arrow" keypress).

// Let's imagine you've defined this somewhere, and it blocks for
// user input then transforms that into an Action
Action AcceptUserInput();

class Fruit
{
public:
   Location GetLocation() const;
   void MoveTo(const Location newLocation);

private:
   Location m_location;
};

class Snake
{
public:
   Location GetLocation() const;
   void MoveTo(const Location newLocation);

private:
   Location m_location;
};

class Game
{
public:
   void SetSnakeInitialLocation(const Location snakeLocation)
   {
      m_snake.MoveTo(snakeLocation);
   }
   
   void AddFruit(const Location fruitLocation)
   {
      Fruit newFruit;
      newFruit.MoveTo(fruitLocation);
      
      m_fruits.push_back(newFruit);
   }
   
   void PerformAction(const Action action)
   {
      // If the action is to move the snake, do that by
      // calling things on m_snake, e.g. m_snake.MoveTo(newLocation)
      // Otherwise, do whatever needed.
      
      // Now, let's examine the state of the board. We have
      // all the information needed to do that, and don't have
      // to obtain it from anywhere else.
      for (Fruit& fruit : m_fruits)
      {
          if (m_snake.Intersects(fruit))
          {
             // Grow the snake; remove the fruit
             // 
             // (N.B. you can't use ranged-for like this if you're
             // going to erase from m_fruits, but that detail is out
             // of scope of this answer)
          }
      }
   }

private:
   std::vector<Fruit> m_fruits;
   Snake m_snake;
};

int main()
{
   Game game;
   game.AddFruit(Location{20, 15});
   game.SetSnakeInitialLocation(Location{0, 0});
   
   while (true)
   {
      Action nextAction = AcceptUserInput();
      game.PerformAction(nextAction);
   }
}