C++ vector的漏洞可導致double free

在《STL 源碼剖析 -- 侯捷》書籍分析的 tass-sgi-stl-2.91.57-source 源碼中。

對 vector 的 erase(); 函數有疑問:

  1. iterator erase(iterator position) {
  2. if (position + 1 != end())
  3. {
  4. //把從 position+1 到 finish 之間的元素一個一個複製到從 position 指向
  5. //的空間,這樣,就把 position 原來指向的元素個覆蓋了
  6. copy(position + 1, finish, position);
  7. }
  8. --finish;
  9. destroy(finish);
  10. return position;
  11. }



  //=====================================================================
  //該函數把 position 指向的對象刪除
  //但是,注意(2013-6-24 wkf):
  //自己認爲該函數有一個漏洞,就是: 該函數沒有真正地銷燬 position 指向的對象,
  //沒有執行它的析構函數。
  //假設有如下的一個列表,其內存空間表示如下:
  //---------------------------------------------------------
  //| 1 | 2 | 3 | 4 | 5 |  |  |
  //---------------------------------------------------------
  //           |
  //                  finish
  //現在,要刪除第二個元素 2 這個對象。
  //所以,執行 copy 之後,把 3, 4, 5 對象往前移動,其內存空間如下:
  //---------------------------------------------------------
  //| 1 | 3 | 4 | 5 | 5 |  |  |
  //---------------------------------------------------------
  //           |
  //                 finish
  //注意,第 5 個元素的內存空間還是原來的對象 5 這個對象。
  //然後,執行 --finish; 操作,移動指針,如下:
  //---------------------------------------------------------
  //| 1 | 3 | 4 | 5 | 5 |  |  |
  //---------------------------------------------------------
  //         |
  //               finish  
  //然後,執行 destroy(finish); 操作,執行的是第 5 個元素的析構函數,而
  //我們刪除的第2個對象,並沒有執行其析構函數,只是把它在內存空間給覆蓋了。
  //如果一個對象中有 動態分配 的內存空間,就不會被釋放。
  //因爲,我們都習慣於在 構造函數中使用 new 來分配內存,在析構函數中使用 delete 來
  //釋放內存。
  //=====================================================================

如下是一個測試的的例子:

  1. class test
  2. {
  3. public:
  4. int i;
  5. public:
  6. test(int a)
  7. {
  8. i = a;
  9. cout << "construct i = " << i << endl;
  10. }
  11. test(const test &a)
  12. {
  13. i = a.i;
  14. cout << "copy construct i = " << i << endl;
  15. }
  16. ~test()
  17. {
  18. cout << "=== destruct i = " << i << endl;
  19. }
  20. };
  21. void show(vector<test>& num)
  22. {
  23. vector<test>::iterator index;
  24. index = num.begin();
  25. while(num.end() != index)
  26. {
  27. cout << (*index).i << " ";
  28. index++;
  29. }
  30. cout << endl;
  31. }
  32. void main()
  33. {
  34. vector<test> num;
  35. for(int i = 0; i < 6; i++)
  36. {
  37. num.push_back(test(i));
  38. }
  39. show(num);
  40. num.erase(num.begin()+1);
  41. show(num);
  42. num.erase(num.begin()+1);
  43. show(num);
  44. num.erase(num.begin()+1);
  45. show(num);
  46. printf("hehe.....\n");
  47. getchar();
  48. return;
  49. }

運行的結果如下:

construct i = 0
copy construct i = 0
=== destruct i = 0
construct i = 1
copy construct i = 0
copy construct i = 1
=== destruct i = 0
=== destruct i = 1
construct i = 2
copy construct i = 0
copy construct i = 1
copy construct i = 2
=== destruct i = 0
=== destruct i = 1
=== destruct i = 2
construct i = 3
copy construct i = 0
copy construct i = 1
copy construct i = 2
copy construct i = 3
=== destruct i = 0
=== destruct i = 1
=== destruct i = 2
=== destruct i = 3
construct i = 4
copy construct i = 0
copy construct i = 1
copy construct i = 2
copy construct i = 3
copy construct i = 4
=== destruct i = 0
=== destruct i = 1
=== destruct i = 2
=== destruct i = 3
=== destruct i = 4
construct i = 5
copy construct i = 5
=== destruct i = 5
0  1  2  3  4  5
=== destruct i = 5
0  2  3  4  5
=== destruct i = 5
0  3  4  5
=== destruct i = 5
0  4  5
hehe.....

重點是後面的輸出:

=== destruct i = 5
0  1  2  3  4  5
=== destruct i = 5
0  2  3  4  5
=== destruct i = 5
0  3  4  5
=== destruct i = 5
0  4  5

執行的析構函數並不是需要刪除的那個對象。

 

 

 

 

 

 

 

 

 

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