简体   繁体   English

嵌入式C ++-多态性和继承

[英]Embedded C++ - polymorphism and inheritance

I'm writing a game (space invaders) on an Arduino which is connected to a graphical LCD, and I have a sprite class. 我正在连接至图形LCD的Arduino上编写游戏(太空入侵者),并且有一个Sprite类。 This class has attributes such as Player/Alien, a bitmap object, location(x,y), and a how to move function. 此类具有诸如Player / Alien,位图对象,location(x,y)和“如何移动”功能的属性。

I want each instance to have a missile and I think this may be done by inheritance and polymorphism, though I'm unsure how -- my streamlined code is below and to give a better idea as to shapes I've included a glyph image. 我希望每个实例都有一枚导弹,我认为这可以通过继承和多态性来完成,尽管我不确定如何-我的精简代码在下面,并对包含字形图像的形状给出了更好的主意。 I'd like Missile to derive location(x,y) from the sprite class, but it'll have its own bitmap and method of movement, something like(?) 我想让Missile从sprite类派生location(x,y),但是它会有自己的位图和移动方法,类似于(?)

Class Missile: public Sprite{
  Missile();   // create shape here
  void Move(); // has its own method of moving, but starts from Sprite(x,y)  
};  

[Regardless of how to do this, I'd like to use inheritance and polymorphism for my C++ practice please] [无论如何执行,请在我的C ++实践中使用继承和多态性]

Adafruit_PCD8544 display = Adafruit_PCD8544(7, 6, 5, 4, 3);
unsigned char spaceShip[5]    PROGMEM = {0x3c, 0x1e, 0x1f, 0x1e, 0x3c};
unsigned char spaceAlien[5]   PROGMEM = {0x1e, 0x0f, 0x1f, 0x0f, 0x1e};
unsigned char spaceMissile[5] PROGMEM = {0x00, 0x00, 0x1f, 0x00, 0x00}; 

enum TYPES {ALIEN = 0, PLAYER = 1 };
class Sprite
{
  public:
    Sprite(TYPES Type);
    void Move();
    void Render()      { display.drawBitmap(x,y, spacePtr, 5, 6, BLACK); }
  private:
    unsigned char *spacePtr;
    unsigned int x, y;
    TYPES Type;
};

Sprite::Sprite(TYPES theType)
{
  Type   = theType;
  switch( Type )
  {
      case( PLAYER ):
        spacePtr = &spaceShip[0];
        x = xPlayer(); // get x from xfunction
        y = yPlayer(); // get y from yfunction
        break;      
      case( ALIEN ):
        spacePtr = &spaceAlien[0];
        x = random(0, 82);
        y = random(10, 20);
        break;
      default: 
        break;
   }
}

位图在这里

Before getting the missile involved, you should realize that your current implementation of sprite could in fact be split into (at least) three classes: A Player, an Alien and a Sprite from which the former two derive. 在使导弹介入之前,您应该意识到,实际上,您当前执行的sprite实际上可以分为(至少)三类:一个Player,一个Alien和一个Sprite,前两者是从中派生的。

The point of inheritance is that it represents a "is a" relationship. 继承的要点是它表示“是”关系。 Ie: a Player IS A Sprite, an Alien IS A Sprite. 即:玩家是精灵,外星人是精灵。 In this case, a sprite is a class that can be moved, rendered, has a position and a bitmap. 在这种情况下,子画面是可以移动,渲染,具有位置和位图的类。 As you show it there, the things that distinguish an Alien from a Player is its initial position the location of its bitmap data and presumably the way it moves. 如您在此处所示,将外星人与播放器区分开来的是它的初始位置,位图数据的位置以及可能的移动方式。

class Sprite
{
  public:
    virtual ~Sprite();
    Sprite(unsigned char * const spacePtrIn, unsigned int xInit, unsigned int yInit)
        : spacePtr(spacePtrIn)
        , x(xInit)
        , y(yInit)
    {}
    virtual void Move() = 0;
    void Render(Display &display) const { display.drawBitmap(x,y, spacePtr, 5, 6, BLACK); }
    unsigned int X() const {return x;} 
    unsigned int Y() const {return y;}
  protected:
    void X(int newX) { x = newX; }
    void Y(int newY) { y = newY; }
  private:
    unsigned char * const spacePtr;
    unsigned int x, y;
};

class Alien : public Sprite
{
public:
   Alien()
     : Sprite(spaceAlien, random(0, 82), random(10, 20))
   {}
   virtual void Move();
};

class Player : public Sprite
{
public:
   Player()
     : Sprite(spaceShip, xPlayer(), yPlayer())
   {}
   virtual void Move();
};

Once we have separated the specialized properties of players and aliens from the general properties of a Sprite, it should make it clearer how a missile relates to a sprite: It IS one. 一旦我们将游戏者和外星人的特殊属性与Sprite的常规属性分开,就应该更清楚地说明导弹与Sprite的关系:它是一个。

class Missile : public Sprite
{
public:
   Missile(Sprite const &launchPoint)
     : Sprite(spaceMissile, launchPoint.X(), launchPoint.Y())
   {}
   virtual void Move();
};

The assumption is that once the missile originates from the specified Sprite it has nothing further to do with it. 假设是,一旦导弹从指定的Sprite发出,就不再与它有任何关系。 Note that there is no dependency between Missiles and Aliens/Players. 请注意,导弹与外星人/玩家之间没有依赖关系。

Other points to note is that the x and y attributes of the Sprite can only be modified by the sub-classes through protected setter functions, leaving the Sprite class to range check and/or store the values in a different format if it desires - this is the principle of encapsulation at work. 其他需要注意的是,子类只能通过受保护的setter函数来修改Sprite的x和y属性,如果需要,可以让Sprite类进行范围检查和/或以其他格式存储值-这是封装工作的原理。 Also, I have removed the reference to a presumably global display object - instead it is passed into the now const Render function when required. 另外,我删除了对大概是全局显示对象的引用-而是在需要时将其传递到现在的const Render函数中。 This is good for a raft of reasons, not least the general evilness of globals and the hidden dependencies they introduce. 这样做有很多原因,尤其是全局变量的普遍弊端以及它们引入的隐藏依赖性。 By making the function const, the caller can more readily assume that once the function returns, the display will not be touched by that object until the next call. 通过使函数为const,调用者可以更容易地假定一旦函数返回,该显示将直到下一次调用时才被该对象触摸。 The overhead of doing this is likely to be very low so the fact you are doing it on an Arduino shouldn't deter you, likewise with getter/setter functions as the compiler will most likely optimize them away. 这样做的开销可能会很低,因此您在Arduino上执行操作的事实不应该阻止您,例如使用getter / setter函数,因为编译器很可能会对其进行优化。

Polymorphism comes into this because the code invoking the Render and Move methods doesn't need to know what the actual type of the objects it's dealing are in order to do so - all it needs to know about are sprites. 之所以会涉及多态,是因为调用Render和Move方法的代码不需要知道要处理的对象的实际类型是什么-它需要了解的只是sprite。

void MoveAndRender(Sprite **begin, Sprite **end, Display &display) 
{
   for(; begin != end; ++begin)
   {
      (*begin)->Move();
      (*begin)->Render(display);
   }
}

Of course there are an infinite number of ways of tackling this problem and the hardest bit is defining the problem in the first place. 当然,有无数种解决此问题的方法,最困难的一点是首先定义问题。 This is just a demonstration of how inheritance might fit your scenario. 这只是继承如何适合您的方案的演示。

Inheritance is only one of a number of OO relationships and is often overused or misused, resulting some truly hideous code. 继承只是许多OO关系中的一种,并且经常被过度使用或滥用,从而导致一些真正的丑陋代码。 Familiarize yourself with composition ("has a"), aggregation ("shares a"), and association ("knows a"?) as well. 还要熟悉组成(“具有”),聚合(“共享”)和关联(“知道”?)。 Often a mix of inheritance and composition is far more effective than inheritance alone (see Bridge pattern, Proxy pattern and friends.) 通常,继承和组合的混合比单独继承要有效得多(请参阅桥模式,代理模式和朋友。)

Edit Removed the Type attribute since the actual object type should be all that is needed to characterize its behaviour - this is point, really. 编辑删除了Type属性,因为实际对象类型应该是表征其行为所需要的全部-实际上,这就是重点。 Added MoveAndRender example. 添加了MoveAndRender示例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM