2015-10-07 31 views
-1

這是我製作的一個簡單遊戲中的一段代碼。這個例程檢查數組中的每個子彈或敵人,如果它們已經經過了屏幕,則將它們從數組中移除。 (此代碼使用SDL2,但我不認爲它很重要。)這段代碼有什麼問題?似乎指針突然指向空值

void cleanEnemies(Enemy* a[], int* s) 
{ 
    Enemy* e; 
    for (int i = 0; i < *s; i++) 
    { 
     e = a[i]; 
     if (e->getX() < -75) 
     { 
      e->~Enemy(); 
      e = NULL; 
      for (int j = i; j < *s; j++) 
      { 
       a[j] = a[j + 1]; 
      } 
      (*s)--; 
     } 
    } 
    printf("Enemy count: %i\n", *s); 
} 

void cleanMissiles(HomingMissile* a[], int* s) 
{ 
    HomingMissile* m; 
    for (int i = 0; i < *s; i++) 
    { 
     m = a[i]; 
     if (m->getX() < -75) 
     { 
      m->~HomingMissile(); 
      m = NULL; 
      for (int j = i; j < *s; j++) 
      { 
       a[j] = a[j + 1]; 
      } 
      (*s)--; 
     } 
    } 
    printf("Missile count: %i\n", *s); 
} 

運行程序產生指向無效的內存位置的指針。任何人都可以告訴我我做錯了什麼?謝謝。

+5

爲什麼你明確地打電話給t他析構函數在'm->〜HomingMissile();'而不是使用'delete'? – BlackDwarf

+1

同上'e->〜Enemy()'?同時指定哪些指針指向無效的內存位置會有所幫助。沒有獨立的例子就很難調試。也許析構函數有副作用? – abligh

+2

'std :: vector'和* erase remove idiom *可能有幫助。 – Jarod42

回答

1

問題的原因是(我認爲)內部for循環超出了數組的界限。

void cleanMissiles(HomingMissile* a[], int* s) 
{ 
    HomingMissile* m; 
    // Suggests there are *s entries, so last entry is (*s)-1 
    for (int i = 0; i < *s; i++) 
    { 
     m = a[i]; 
     if (m->getX() < -75) 
     { 
      m->~HomingMissile(); 
      m = NULL; 
      // Last value of j is (*s) - 1 
      for (int j = i; j < *s; j++) 
      { 
       // Here it copies into a[(*s)] -1 from [a(*s)] which is invalid 
       a[j] = a[j + 1]; 
      } 
      (*s)--; 
     } 
    } 
    printf("Missile count: %i\n", *s); 
} 

一個固定的版本將如下所示(依賴於一個事實,即如果循環條件不滿足就進入了for循環將不會執行):

void cleanMissiles(HomingMissile* a[], int* s) 
{ 
    HomingMissile* m; 
    for (int i = 0; i < *s; i++) 
    { 
     m = a[i]; 
     if (m->getX() < -75) 
     { 
      m->~HomingMissile(); 
      m = NULL; 
      for (int j = i + 1; j < *s; j++) 
      { 
       a[j - 1] = a[j]; 
      } 
      (*s)--; 
     } 
    } 
    printf("Missile count: %i\n", *s); 
} 

但不是解決這個問題,請讓你的生活更輕鬆,並切換到std::vector或更好的std::list,並調用delete(假設你分配了new)而不是直接調用析構函數。

+2

不要建議調用'delete',直到你確定要刪除的對象是使用'new'分配的。它可以是來自靜態分配數組的項目。 – axiac

+0

@axiac他之前直接調用析構函數。鑑於具體情況,我無法想象這可能是正確的。我建議這樣做的做法是轉移到'std :: vector',這樣做可能性更小。但我會修改文字。 – abligh

+0

我不會使用刪除,使用unique_ptr – paulm

0

隨着std::vector<std::unique_ptr<T>>,就成了出頭,如:

void cleanEnemies(std::vector<std::unique_ptr<Enemy>>& enemies) 
{ 
    auto it = std::remove_if(enemies.begin(), enemies.end(), 
          [](const auto& enemy) { 
            return enemy->getX() < -75; 
          }); 
    enemies.erase(it, enemies.end()); 
    std::cout << "Enemy count: " << enemies.size() << std::endl; 
} 

你甚至可以做一個模板來處理雙方EnemyHomingMissile

template <typename T> 
void cleanObjects(std::vector<std::unique_ptr<T>>& objects, const std::string& objectName) 
{ 
    auto it = std::remove_if(objects.begin(), objects.end(), 
          [](const auto& object) { 
            return object->getX() < -75; 
          }); 
    objects.erase(it, objects.end()); 
    std::cout << objectName << " count: " << objects.size() << std::endl; 
} 

然後cleanEnemies/cleanMissiles可以改寫爲:

void cleanEnemies(std::vector<std::unique_ptr<Enemy>>& enemies) 
{ 
    cleanObjects(enemies, "Enemy"); 
} 

void cleanMissiles(std::vector<std::unique_ptr<HomingMissile>>& missiles) 
{ 
    cleanObjects(missiles, "Missile"); 
}