嵌入式C ++ - 多态和继承 [英] Embedded C++ - polymorphism and inheritance

查看:159
本文介绍了嵌入式C ++ - 多态和继承的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在一个连接到图形LCD的Arduino上写一个游戏(太空入侵者),我有一个sprite类。此类具有诸如Player / Alien,位图对象,位置(x,y)以及如何移动功能的属性。



我希望每个实例具有导弹,我认为这可以通过继承和多态来完成,虽然我不确定如何 - 我的简化代码在下面,并给出一个更好的想法,我已经包含一个字形图像的形状。我想要导弹从精灵类派生位置(x,y),但它会有自己的位图和运动方式,像(?)

  Class Missile:public Sprite {
Missile(); // create shape here
void Move(); //有自己的移动方法,但是从Sprite(x,y)
}开始;

[无论如何做到这一点,我想使用继承和多态来进行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};

枚举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类型;
};

Sprite :: Sprite(TYPES theType)
{
Type = theType;
switch(Type)
{
case(PLAYER):
spacePtr =& spaceShip [0];
x = xPlayer(); //从xfunction获取x
y = yPlayer(); //从y函数获取y
break;
case(ALIEN):
spacePtr =& spaceAlien [0];
x = random(0,82);
y = random(10,20);
break;
默认值:
break;
}
}

解决方案

在得到导弹之前,你应该意识到你目前实现的精灵实际上可以分为(至少)三个类:前者是派生的玩家,外星人和雪碧。



继承点是它代表一个是一个关系。即:一个球员是一个雪碧,一个外星人是一个雪碧。在这种情况下,sprite是一个可以被移动,渲染,具有位置和位图的类。当你在那里显示时,区分外星人和玩家的东西是它的位置数据的位置,并且大概是它移动的方式。

 
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(显示和显示)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 =下一页末; }
void Y(int newY){y = newY; }
private:
unsigned char * const spacePtr;
unsigned int x,y;
};

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

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

一旦我们将玩家和外星人的专业属性从Sprite的一般属性中分离出来,它应该更清晰导弹与小精灵有何关系:它是一个。

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

假设一旦导弹来自指定的Sprite,它与此无关。注意,导弹和外星人/玩家之间没有依赖关系。



其他要注意的是,Sprite的x和y属性只能被子目录修改,类通过受保护的设置器功能,如果需要,则使Sprite类进行范围检查和/或以不同的格式存储值 - 这是在工作中封装的原理。此外,我已经删除了一个可能是全局显示对象的引用,而是在需要时被传递到现在的const Render函数中。这是一个很好的理由,尤其是全局性的一般邪恶和他们介绍的隐藏的依赖。通过使函数const,调用者可以更容易地假设一旦函数返回,则直到下一次调用该显示才会被该对象所接触。这样做的开销可能非常低,所以在Arduino上做的事情不应该阻止你,同样的getter / setter函数,编译器最有可能优化它们。



多态是因为调用Render和Move方法的代码不需要知道它所处理的对象的实际类型是为了这样做 - 所有它需要知道的是

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

当然有无数的方法来解决这个问题,最难的是首先定义问题。这仅仅是一个继承可能适合您的方案的示范。



继承只是OO关系中的一种,经常被滥用或滥用,导致一些真正可怕的代码。熟悉组合(有一个),聚合(共享)和关联(知道?)。通常,遗传和构图的混合比单独的遗传更有效(参见桥梁模式,代理模式和朋友。)



编辑已删除类型属性,因为实际的对象类型应该是描述其行为所需要的全部 - 这是真的。添加了MoveAndRender示例。


I'm writing a game (space invaders) on an Arduino which is connected to a graphical LCD, and I have a sprite class. This class has attributes such as Player/Alien, a bitmap object, location(x,y), and a how to move function.

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(?)

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]

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.

The point of inheritance is that it represents a "is a" relationship. I.e.: 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.

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. 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. Also, I have removed the reference to a presumably global display object - instead it is passed into the now const Render function when required. 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. 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.

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.

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. 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. Added MoveAndRender example.

这篇关于嵌入式C ++ - 多态和继承的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆