我在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对象后,似乎会立即调用析构函数。我在这里错过了什么?我也尝试在堆上分配_sprite
MapCell
成员,但它会导致同样的问题。据我所知,它不会超出范围,因为创建的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;
}
答案 0 :(得分:3)
问题是作业x._sprite = Sprite(...)
。这会创建一个Sprite
临时文件,将其字段复制到_sprite
,然后销毁临时。此外,它在执行赋值之前不在_sprite
上调用析构函数,因此旧的_texture
只会泄漏。
如果您想避免这种情况,请在.set
上设置.load
或Sprite
功能来更新Sprite
的内容而不是复制,并创建私有赋值和复制构造方法,以避免意外滥用。
答案 1 :(得分:2)
for(int i = 0; i < path.GetCount(); i++)
PathNodes.Add(Sprite(*path(i)*32, "Assets/Graphics/PathNode.bmp"));
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// a temporary here!
当该表达式结束时,临时Sprite
的析构函数被调用,释放表面并使用悬空指针离开PathNodes
对象到释放的表面。你的代码中可能还有更多这样的表达式。
服从The Rule of Three并为Sprite
类编写适当的复制构造函数和赋值运算符。
请参阅SDL_Surface
的{{3}}以查看需要执行的操作(您可能需要手动增加曲面的refcount
成员。)