C++里使用vector中的iterator遍歷時需要注意的問題


------------恢復內容開始------------

遇到的實際問題:

最近編程遇到了一個非常有趣的問題,編程題目里詢問在 Game::update() 里加入一個 entity 之后,這個 entity 是在加入的這一幀更新還是在下一幀(比如在這一幀里玩家生成了一個子彈,這個子彈的位置是在這一幀更新,還是下一幀)?

於是我就寫了一個簡化的獨立測試,代碼如下

(方便起見,用了全局變量)

 1 std::vector<int> v;
 2 
 3 void addInteger(int i)
 4 {
 5     v.push_back(i);
 6 }
 7 
 8 int main()
 9 {
10     addInteger(0);
11     addInteger(1);
12     addInteger(2);
13     auto it = v.begin();
14     while (it != v.end())
15     {
16         std::cout << (*it) << std::endl;
17         if ((*it) == 1)
18             addInteger(3);
19         it++;
20     }
21     return 0;
22 }
View Code

這里面我想的是用 

1 std::cout << (*it) << std::endl;

代替調用iterator的部分代碼,用 if 部分代替滿足某種條件后的加入entity操作

之后就有了一個神奇的報錯 “can't increment vector iterator past end”

但是我在主項目里的代碼並沒有這個報錯,我也很確認,的確是向里面加入了一個新的 entity 的。

在詢問教授前,我對這個問題有一些想法,很可能是由於向vector里加入了新的元素,導致vector之前分配的空間不夠了,需要重新尋找並分配空間(這是vector比較低效的一個原因)所以iterator這個指針丟失了。

為了驗證這個猜想,我把代碼寫成了這樣

 1 std::vector<int> v;
 2 
 3 void addInteger(int i)
 4 {
 5     v.push_back(i);
 6 }
 7 
 8 int main()
 9 {
10     v.reserve(4);
11     addInteger(0);
12     addInteger(1);
13     addInteger(2);
14     auto it = v.begin();
15     while (it != v.end())
16     {
17         std::cout << (*it) << std::endl;
18         if ((*it) == 1)
19             addInteger(3);
20         it++;
21     }
22     return 0;
23 }
View Code

加入了 v.reserve(4) 后果然不報錯了,此時初步確認這個問題就是因為 vector 的地址重新分配導致的。之后導師給我提供了更多的思路,需要區分 vector 的 size() 和 capacity() 這兩個概念,大小應該是當前vector所擁有的元素多少,而容量才是當前vector分配的內存空間。於是我做了簡單的測試,來測試 vector 會在哪幾個條件下改變capacity

 1 void charCapacityTester(int size=64)
 2 {
 3     std::vector<char> c;
 4 
 5     int preCap = c.capacity();
 6     for (int i = 0; i < size; ++i)
 7     {
 8         if (preCap != c.capacity())
 9         {
10             std::cout << "Size: " << c.size() << " Capacity: " << c.capacity() << std::endl;
11             preCap = c.capacity();
12         }
13         c.push_back('a');
14     }
15 }
View Code

輸出結果如圖

 

 所以當元素很少的時候,或者說vector增長很快的時候,vector 會頻繁重新分配,這就導致了效率低下和 iterator 不安全。解決辦法目前想到有幾種

1. 根據需要預分配

其實就是我在獨立測試里用的reserve方法,比如根據這個變化趨勢和我游戲里entity的數量,可能預分配64的空間比較合適,因為64正好超過了63(不會因為加入一個新的entity就崩潰)而且和我實際的entity數量匹配。而且這個可以避免頻繁的重新分配vector空間。

2. 不用iterator

如果不想提前reserve的話也可以改用普通的for循環

1 for(int i = 0; i < v.size(); ++i)
2 {
3      if(v[i] == 1)
4     {
5         v.push_back(3);
6     }
7 }

這樣也能避免指針丟失。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM