《遊戲編程入門》源代碼子畫面銷燬bug與vector容器vector::erase的問題

原書中GameEngine的代碼有兩處bug,源代碼分別爲:

void GameEngine::CleanupSprites()
{
  // Delete and remove the sprites in the sprite vector
  vector<Sprite*>::iterator siSprite;
  for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++)
  {
    delete (*siSprite);
    m_vSprites.erase(siSprite);
    siSprite--;
  }
}
void GameEngine::UpdateSprites()
{
  // Check to see if the sprite vector needs to grow
  if (m_vSprites.size() >= (m_vSprites.capacity() / 2))
    m_vSprites.reserve(m_vSprites.capacity() * 2);

  // Update the sprites in the sprite vector
  RECT          rcOldSpritePos;
  SPRITEACTION  saSpriteAction;
  vector<Sprite*>::iterator siSprite;
  for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++)
  {
    // Save the old sprite position in case we need to restore it
    rcOldSpritePos = (*siSprite)->GetPosition();

    // Update the sprite
    saSpriteAction = (*siSprite)->Update();

    // Handle the SA_KILL sprite action
    if (saSpriteAction & SA_KILL)
    {
		
      // Notify the game that the sprite is dying
      SpriteDying(*siSprite);

      delete (*siSprite);
      m_vSprites.erase(siSprite);
      siSprite--;
      continue;
    }

    // See if the sprite collided with any others
    if (CheckSpriteCollision(*siSprite))
      // Restore the old sprite position
      (*siSprite)->SetPosition(rcOldSpritePos);
  }
}

這兩段代碼在VC6編譯器能正常運行,但在VS2008以後的編譯器編譯之後運行時產生錯誤,分別修正如下:

void GameEngine::CleanupSprites()
{
  // Delete and remove the sprites in the sprite vector
  vector<Sprite*>::iterator siSprite;
  for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end(); siSprite++)
  {
    delete (*siSprite);//注意這裏
  }
  m_vSprites.clear();//注意這裏
}
//=================================================
//或
void GameEngine::CleanupSprites()
{
 // Delete and remove the sprites in the sprite vector
 vector<Sprite*>::iterator siSprite;
 for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end();)//注意這裏
 {
  delete (*siSprite);
  siSprite = m_vSprites.erase(siSprite); //注意這裏
 }
}
//=====================================================

void GameEngine::UpdateSprites()
{
	// Check to see if the sprite vector needs to grow
	if (m_vSprites.size() >= (m_vSprites.capacity() / 2))
	{
		m_vSprites.reserve(m_vSprites.capacity() * 2);
	}
	// Update the sprites in the sprite vector
	RECT          rcOldSpritePos;
	SPRITEACTION  saSpriteAction;

	vector<Sprite*>::iterator siSprite;
	for (siSprite = m_vSprites.begin(); siSprite != m_vSprites.end();)//注意這裏
	{
		// Save the old sprite position in case we need to restore it
		rcOldSpritePos = (*siSprite)->GetPosition();

		// Update the sprite
		saSpriteAction = (*siSprite)->Update();

		// Handle the SA_KILL sprite action
		if (saSpriteAction & SA_KILL)
		{

			// Notify the game that the sprite is dying
			SpriteDying(*siSprite);
			delete (*siSprite);
			//siSprite--;
			siSprite = m_vSprites.erase(siSprite);	//注意這裏
			continue;
		}

		// See if the sprite collided with any others
		if (CheckSpriteCollision(*siSprite))
		{
			// Restore the old sprite position
			(*siSprite)->SetPosition(rcOldSpritePos);
		}
		siSprite++;//注意這裏
	}

}

分析:vector.erase(p)之後,所有指向p所指的內存的迭代器全部失效,但是它的返回值是指向下一個元素的迭代器。以前的編譯器之所以能夠成功運行,應該是之前的標準並不清晰或者編譯器內部實現的問題,VS2008之後嚴格按照標準來之後就出現運行時錯誤。


PS :

 《BEGINNING GAME PROGRAMMING》 中還有個問題:程序要是換用了修改了BMP圖像資源,結果是圖像不能正常顯示變黑的了。 

http://blog.csdn.net/dkink/article/details/2310564  給出瞭解決辦法,但還有點小問題,下面給出原因和改正辦法。

這裏作者忽略了一個小小的問題,...........無壓縮BMP文件的
pBitmapInfo->bmiHeader.biSizeImage 裏面的值不一定是圖像的真實大小,可能是0或者隨意的值。 所以需要重新計算,在Bitmap.cpp裏面,Bitmap類的 兩個Create函數的下面這個位置添加計算biSizeImage的代碼。

// Store the width and height of the bitmap
  BITMAPINFO* pBitmapInfo = (BITMAPINFO*)pBitmapImage;
  m_iWidth = (int)pBitmapInfo->bmiHeader.biWidth;
  m_iHeight = (int)pBitmapInfo->bmiHeader.biHeight;

// 計算biSizeImage填充回去,是增加的代碼 

//注意這裏
int lineByte=(m_iWidth*pBitmapInfo->bmiHeader.biBitCount/8+3)/4*4; //位圖每行佔多少個字節
pBitmapInfo->bmiHeader.biSizeImage = m_iHeight*lineByte;

///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////


  // Get a handle to the bitmap and copy the image bits
  PBYTE pBitmapBits;
  m_hBitmap = CreateDIBSection(hDC, pBitmapInfo, DIB_RGB_COLORS,
    (PVOID*)&pBitmapBits, NULL, 0);

參考資料:

1.C++Primer中文版(第四版)P282~283




2.http://www.cplusplus.com/forum/beginner/47420/

3.http://blog.csdn.net/dkink/article/details/2330905

4.http://blog.csdn.net/vhshiwen/article/details/4808867

5.http://www.cnblogs.com/buxianghe/archive/2012/06/25/2560713.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章