使用抽象基类发出指向SDL_Rect类成员的指针

时间:2014-08-20 20:24:33

标签: c++ polymorphism sdl

我有一个带有一些继承者的抽象基类,我创建了一个指向继承者的抽象基类的指针数组:

抽象基类:

class Tile              
{
    public:

        bool isPassable;
        void setTileSet( SDL_Texture *Texture );

        SDL_Rect* clip;

};

继承:

class Floor: public Tile
{
    public:

        bool isPassable = true;
        SDL_Rect clip = { 20, 0, 20, 20 };

};

指针数组:

Tile * dungeon[ z ][ x ][ y ] = {{{ nullptr }}};

当我尝试通过指针数组访问成员SDL_Rect剪辑以blit一个tile时,没有任何反应。该程序编译并运行,但屏幕保持默认黑色:

dungeon[ 0 ][ 0 ][ 0 ] = &floor;

Draw::renderTexture( TILESET, REN, 0, 0, dungeon[ 0 ][ 0 ][ 0 ]->clip );

但是,当我只使用Floor类访问剪辑时,图像会很好地显示:

Draw::renderTexture( TILESET, REN, 0, 0, floor.clip );

我认为指针抽象基类可能没有加载,但是当我试图将代码写出去除掉它时:

SDL_Rect error = { 0, 0, 20, 20 };

if( dungeon[ 0 ][ 0 ][ 0 ] != nullptr )
{
    Draw::renderTexture( TILESET, REN, 0, 0, dungeon[ 0 ][ 0 ][ 0 ]->clip );   
}
else
{
    Draw::renderTexture( TILESET, REN, 0, 0, &error );                          
}

屏幕仍为黑色。

这是Draw :: renderTexture函数,虽然我怀疑它是问题所在:

void Draw::renderTexture(SDL_Texture *texture, SDL_Renderer *render, int x, int y, SDL_Rect *clipping )     
{

SDL_Rect destination;
destination.x = x;
destination.y = y;
destination.w = clipping->w;
destination.h = clipping->h;

SDL_RenderCopy( render, texture, clipping, &destination ); 

2 个答案:

答案 0 :(得分:0)

您需要首先转换数组成员,以便它知道它正在查看哪个成员。目前,您有一个Tile个实例数组,因此当您将dungeon[ 0 ][ 0 ][ 0 ]->clip传递给渲染函数时,系统会假定您的意思是基类。

如果您将其更改为(Floor*)(dungeon[ 0 ][ 0 ][ 0 ])->clip,则应该有效。

但是我会指出这看起来像一个非常糟糕的设计,可能是因为你还没有完全理解继承和多态,尽管你可能有一些理由按照你的方式去做。

我个人会将类更改为如下,首先是基类:

class Tile              
{
    public:
        Tile() :
            m_passable(true) {}
        Tile(bool passable, SDL_Rect& clip) :
            m_passable(passable),
            m_clip(clip) {}

        virtual ~Tile() {}

        inline void SetTileSet(SDL_Texture *texture) { m_texture = texture; }
        inline SDL_Texture* GetTileSet() { return m_texture; }

        inline void SetClip(SDL_Rect& clip) { m_clip = clip; }
        inline SDL_Rect& GetClip() { return m_clip; }

        inline void SetPassable(bool passable) { m_passable = passable; } 
        inline bool IsPassable() { return m_passable; }

    protected:  
        bool m_passable;
        SDL_Rect m_clip;
        SDL_Texture* m_texture;

    private:
        Tile(Tile const&) = delete;
        Tile& operator=(Tile const&) = delete;
};

然后是继承类:

class Floor : public Tile
{
    public:
        Floor(bool passable, SDL_Rect& clip) :
            Tile(passable, clip);

    protected:
    private:
        Floor(Floor const&) = delete;
        Floor& operator=(Floor const&) = delete;
};

然后,如果您想要新的Floor实例,请执行以下操作:

SDL_Rect clip = { 0, 0, 20, 20 };
Floor* myFloor = new Floor(true, clip);

然后只需执行以下操作即可将其放入数组中:

dungeon[ 0 ][ 0 ][ 0 ] = (Tile*)myFloor;

最后,如果进行这些更改,则应通过引用传递渲染函数:

void Draw::renderTexture(SDL_Texture* texture, SDL_Renderer* render, int x, int y, SDL_Rect& clipping )     
{

    SDL_Rect destination = { x, y, clipping.w, clipping.h };
    SDL_RenderCopy( render, texture, &clipping, &destination );
} 

答案 1 :(得分:0)

在每个楼层,您都有两个版本的isPassable:Floor::isPassableTile::isPassable。一个是初始化而另一个不是。您还有两个版本的剪辑。 Floor::clip未初始化,Tile::clip未初始化。所以当你将你的地牢元素称为Tile时,它会自然地访问未初始化的元素和灾难性攻击。

您所说的代码if( dungeon[ 0 ][ 0 ][ 0 ] != nullptr )...没有帮助,因为Tile::clip不是空的;它没有初始化。

解决方法是消除isPassable并从Tile或Floor中剪辑,因此每个只有一个副本,并且无法访问错误的副本。

如果你想把它保存在Tile中,这些是需要解决的问题:

  • 初始化它们,可能默认为false和NULL,在Tile的ctor中 - 所有类的所有成员都需要初始化。这就是为什么你的if语句没有帮助
  • 如果Floors需要不同的初始值:Floor () : Tile (true, 20, 0, 20, 20) {}
  • 让Floor的ctor调用Tile的ctor,并传递适当的值。