2013-02-27 63 views
0

我正在編程SDL中的基於圖塊的地圖,並且Map類的構造函數正在用於設置用於表示其中包含的每個對象的圖像。然而,我正在用我的Sprite類的析構函數解決這個問題,這個類正在被用來釋放對象持有的SDL_Surface*。析構函數被調用得很早,我不完全確定爲什麼。這是我的Map構造函數的精簡版本,只是顯示瞭如何分配單元格的sprite。SDL析構函數調用太早

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

回答

3

的問題是分配x._sprite = Sprite(...)。這會創建一個臨時的Sprite,將其字段複製到_sprite,然後破壞臨時的。此外,不是在執行任務前調用_sprite的析構函數,因此舊的_texture只會泄漏。

如果你想避免這種情況,對Sprite一個.set.load功能更新Sprite,而不是複製的內容,並創建私人分配和拷貝構造方法,以避免意外的濫用。

+0

準確地說:「3規則」和boost :: noncopyable是這裏的重要部分。 – Roddy 2013-02-27 13:17:29

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類寫一個適當的拷貝構造函數和賦值操作符。

請參閱the documentationSDL_Surface以查看需要完成的操作(您可能需要手動增加表面的refcount成員)。

+0

在這種情況下,爲了避免使用refcounts,簡單地將複製構造函數和賦值方法私有化可能會更簡單。這樣,你可以確保每個紋理只與一個'Sprite'對象相關聯。 – nneonneo 2013-02-27 11:30:23

+1

如果這是可行的OP,當然。或者可能會將具有'SDL_Freesurface'的'_texture''std :: sharer_ptr'作爲刪除器。 – jrok 2013-02-27 11:31:57