SDL析构函数调用太早

SDL Destructor Called too early

本文关键字:太早 函数调用 析构 SDL      更新时间:2023-10-16

我正在SDL中编程一个基于瓦片的映射,Map类的构造函数用于设置用于表示其中包含的每个MapCell对象的映像。但是,我的Sprite类的析构函数有问题,该析构函数用于释放对象所持有的SDL_Surface*。析构函数被提前调用,我不完全确定为什么。这里是我的Map构造函数的精简版本,只是显示了如何分配单元格的精灵。

Map::Map(string fileName, int tileWidth, int tileHeight)
{   
    string mapData = ReadMap(fileName);
    _cells = new MapCell[_width*_height];
    for(int y = 0; y < _height; y++)
    {
        for(int x = 0; x < _width; x++)
        {
            int currentCell = y*_width+x;
            if(mapData[currentCell] == '0' || mapData[currentCell] == 'P' || mapData[currentCell] == 'X')
            {
                _cells[currentCell]._sprite = Sprite(Point2D(x*tileWidth, y*tileHeight), "Assets/Graphics/Grass.bmp");
                if(mapData[currentCell] == 'P')
                    _cells[currentCell]._sprite = Sprite(Point2D(x*tileWidth, y*tileHeight), "Assets/Graphics/Player.bmp");
                if (mapData[currentCell] == 'X')
                    _cells[currentCell]._sprite = Sprite(Point2D(x*tileWidth, y*tileHeight), "Assets/Graphics/Target.bmp");
            }
            else if(mapData[currentCell] == '1')
                _cells[currentCell]._sprite = Sprite(Point2D(x*tileWidth, y*tileHeight), "Assets/Graphics/Wall.bmp");
        }
    }
}

析构函数似乎是在创建Sprite对象后立即调用的。我在这里错过了什么?我也尝试过在堆上分配MapCell_sprite成员,但这会导致同样的问题。据我所知,它并没有超出范围,因为创建的Sprite对象是Map对象的一部分。

以下是我的Sprite类的构造函数和析构函数:

Sprite::Sprite(void)
{
    _texture = NULL;
    _position = Point2D::Zero();
}
Sprite::Sprite(Point2D position, std::string texPath)
{
    _texture = Content::LoadBMP(texPath);
    _position = position;
}
Sprite::~Sprite(void)
{
    SDL_FreeSurface(_texture);
}

如果有帮助的话,这是我的主菜单:

int main( int argc, char* args[] )
{
    const int TILEWIDTH = 32;
    const int TILEHEIGHT = 32;
    // Initialization
    InitSDL();
    Map map = Map("Assets/Maps/Map3.txt", TILEWIDTH, TILEHEIGHT);
    Window::SetSize(Rectangle(0, 0, map.GetWidth()*TILEWIDTH, map.GetHeight()*TILEHEIGHT));
    PathFinder pathFinder = PathFinder();
    List<Point2D> path = pathFinder.FindPath(map, map.GetPlayerStart(), map.GetTarget());
    List<Sprite> PathNodes = List<Sprite>();
    for(int i = 0; i < path.GetCount(); i++)
        PathNodes.Add(Sprite(*path(i)*32, "Assets/Graphics/PathNode.bmp"));
    bool quit = false;
    SDL_Event Event;
    while(quit == false)
    {
        while(SDL_PollEvent(&Event))
        {
            if(Event.type == SDL_QUIT)
                quit = true;
        }
        map.Draw();
        for(int i = 0; i < path.GetCount(); i++)
        {
            if(PathNodes(i)->GetPosition() != map.GetPlayerStart()*32 && PathNodes(i)->GetPosition() != map.GetTarget()*32)
                PathNodes(i)->Blit();
        }
        Window::Flip();
    }
    //Quit SDL
    SDL_Quit();
    return 0;    
}

问题是分配x._sprite = Sprite(...)。这将创建一个Sprite临时文件,将其字段复制到_sprite中,然后销毁该临时文件。此外,在执行赋值之前,它确实而不是调用_sprite上的析构函数,所以旧的_texture只会泄漏。

如果您想避免这种情况,请在Sprite上使用.set.load函数来更新Sprite的内容,而不是复制,并创建私有赋值和复制构造函数方法,以避免意外滥用。

for(int i = 0; i < path.GetCount(); i++)
        PathNodes.Add(Sprite(*path(i)*32, "Assets/Graphics/PathNode.bmp"));
                    // ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                    // a temporary here!

当该表达式结束时,将调用临时Sprite的析构函数,释放曲面,并为PathNodes对象留下一个指向已释放曲面的悬空指针。您的代码中可能有更多这样的表达式。

遵循三条规则,为Sprite类编写一个合适的复制构造函数和赋值运算符。

请参阅SDL_Surface的文档以了解需要执行的操作(您可能需要手动增加曲面的refcount成员(。