【问题标题】:Issue with a pointer to a SDL_Rect class member using an abstract base class使用抽象基类指向 SDL_Rect 类成员的指针问题
【发布时间】:2014-09-02 02:35:05
【问题描述】:

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

抽象基类:

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 时,没有任何反应。程序编译并运行,但屏幕保持默认黑色:

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

【问题讨论】:

    标签: c++ polymorphism sdl


    【解决方案1】:

    在每个楼层,您都有两个版本的 isPassable:Floor::isPassableTile::isPassable。一个已初始化,另一个未初始化。您还有两个版本的剪辑。 Floor::clip 未初始化,Tile::clip 未初始化。所以很自然,当您将地牢元素称为 Tile 时,它​​会访问未初始化的元素并发生灾难。

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

    解决方案是从 Tile 或 Floor 中删除 isPassable 和 clip,因此您将只有一个副本,并且不能访问错误的副本。

    如果您想将其保留在 Tile 中,请解决以下问题:

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

    【讨论】:

    • 我假设我应该通过抽象基类 Tile::clip 访问 Floor::clip?如果基类没有成员的副本,我如何指向继承类的成员?
    • 应该是Tile还是Floor的成员,取决于Tile是否需要这些成员。如果有,那么它应该拥有它们,并且 Floor 会继承它们。您引用继承成员的方式只是引用它:例如dungeon[ 0 ][ 0 ][ 0 ]->clip。这里指的是成员clip,不管它是否被继承。
    • 我们也可以讨论您是否应该将这些成员设为私有或受保护,但为了简单起见,我建议您:让这些成员成为 Tile 的副本;将它们从地板上移除;根据需要在适当的ctor中初始化它们;看看情况如何。
    • 谢谢!你对像我这样的菜鸟非常有帮助和清晰:)
    • 我认为这对你有用。伟大的!如果您的问题得到了回答——通过 this 回答——请点击接受。如果已回答,但有其他答案,请单击以接受该答案。
    【解决方案2】:

    您需要先转换您的数组成员,以便它知道它正在查看哪个成员。目前您有一个 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 );
    } 
    

    【讨论】:

      猜你喜欢
      • 2013-01-27
      • 1970-01-01
      • 2011-05-24
      • 1970-01-01
      • 2018-03-24
      • 1970-01-01
      • 2015-07-28
      • 2010-12-02
      • 1970-01-01
      相关资源
      最近更新 更多