STL容器裏存放對象還是指針[僅把圖片敲成文字]

STL容器裏存放對象還是指針
2011-10-12 11:00

轉自鏈接:http://www.oschina.net/question/234345_55497

一.問題的引出:
容器可以存放對象,可以存放指針,這裏要談的是兩者的使用問題。就是什麼時候存放對象更好,什麼時候存放指針更好?

二.問題的分析過程:

1. 首先說下stl容器的工作方式
對於內建類型(int float char等),容器的工作方式是純粹的位拷貝,這裏沒有什麼需要多說的。

對於自定義的對象,容器容納了對象(比如通過insert或push_back等),但容器中存放的對象不是你給它們的那個對象,因爲兩個對象在內存中的位置不一樣。此外,當你從容器中獲取一個對象時,你所得到的對象不是容器裏的那個對象。取而代之的是,當你向容器中添加一個對象(比如通過insert或push_back等),進入容器的是你指定的對象的拷貝。拷進去,拷出來。拷貝是STL的方式。

下面的例子可以說明這點:

int main()
{
    vector
      //我們的對象
      Object data;
      data.setName("Data");
      //對象插入到vector後面
      v.push_back(data);

      cout << "Address of data :          &data = " << &data << " ";
      cout << "Address of data in vector: &v[0] = " << &v[0] << " ";
    system("pause") ;

    return 0;
}

輸出如下:
Address of data:                       &data = 0012FF18
Address of data in vector:             &v[0] = 003A6108

2. 存放對象的情況

明白了容器的工作方式, 那麼進一步來討論容器存放對象和指針在操作過程中的開銷。內建類型的數據進行拷貝的方式是位拷貝,自定義類型的數據進行拷貝會調用類的拷貝構造函數,這個 函數是每個類都有的,如果類中沒有顯式的聲明那麼編譯器也會給提供一個默認的拷貝構造函數。如果一個類的數據非常多,或者包含其他複雜自定義類型,那麼此 時類的拷貝構造的開銷是非常大的。

此時容器中要是存放的是對象vector,那麼一個簡單的插入操作的代價也是驚人的,更別說什麼排序之類的操作,很容易帶來性能上的瓶頸,這個時候就需要在容器中存放對象的指針vector,這樣的話就避免了這些多餘的拷貝消耗,因爲指針就是一個機器字長,拷貝的代價可以忽略不計。

寫了個測試代碼如下:

typedef std::vector ObjectVector;
      typedef std::vector PointerVector;

      //下面是存貯對象的情況
      begin = GetTickCount();
      ObjectVector objectVector;
      for (DWORD i = 0; i < MAX; i++)
      {
           objectVector.push_back(*pCom);
      }

      end = GetTickCount();
      cout << "存放對象消耗的時間:";
      cout << end - begin << "毫秒 ";

      //下面是存貯指針的情況
      begin = GetTickCount();
      PointerVector pinterVector;
      for (DWORD i = 0; i < MAX; i++)
      {
           pinterVector.push_back(pCom);
      }
      end = GetTickCount();

      cout << "存放指針消耗的時間:";
cout << end - begin << "毫秒 ";

下面的結果是在Release版本下,並且編譯器的優化關閉的情況下,這和我們目前的客戶端設置一樣:
MAX = 4000
存放對象消耗的時間:93毫秒
存放指針消耗的時間:0毫秒

MAX = 40000
存放對象消耗的時間:656毫秒
存放指針消耗的時間:16毫秒

MAX = 400000
存放對象消耗的時間:44672毫秒
存放指針消耗的時間:32毫秒

上面的數據沒有用統計學的方法去測試,只是取了一次結果,我測試了多次結果在數量級上是一樣的(用上面的數據只是說明拷貝的代價是巨大的,並沒有強調必須用指針)。

分析完了拷貝的性能消耗,再看看另一個問題,就是聲明瞭一個存放基類對象的容器,如果此時向容器中插入子類的對象,那麼子類特有的那些內容就會被無情剝離(slicing)。這是一個很嚴重的問題。解決的方法還是使用基於指針的容器。

2. 存放指針的情況

   上面提到的兩個問題用指針確實比用對象好,問題不是這麼絕對。在上面考慮拷貝消耗的時候有個前提:如果一個類的數據非常多,或者包含其他複雜自定義類型,並且需要大量的使用需要容器內部對象拷貝的操作。如果一個對象中就是幾個簡單的內建類型,或者乾脆就是一個簡單的內建類型的數據,那麼再用指針可真是得不償失了,因爲使用指針需要程序員去管理內存。完全沒有必要爲了節省幾個int類型的拷貝消耗而去自己去做內存的管理,確實完全沒有必要。

   用指針就需要自己手動的去管理這些指針所指向的內存,stl容器確實可以動態申請內存使自己變大以容納更多的元素,但這些動態空間存放的是你的指針,而並不是你指針指向的動態內存,你的指針內存當然需要你去管理,如果實在不想做這些管理工作,可以去使用智能指針。

三.問題的總結:
通過上面的分析,總結了一下幾點:
1.Stl容器可以存放內建類型、自定義類型、指針類型的元素。
2.元素如果是內置數據類型,那麼就存放數據本身。
3.元素如果是複雜類型,並且在使用容器的過程中需要容器的元素進行大量的拷貝操作的時候,就要考慮在容器中放入指針;
4.存放指針容易出現內存的泄露,所以在使用的時候需要考慮清楚,如能接口設計的合理,能保證容器在使用的過程中不進行大量的拷貝工作,在容器中存放對象是最好的了。
5.使用智能指針是一種兩種優點都兼備的,既有指針的操作效率,又避免了自己手動管理內存帶來的問題。
6.指針可以解決派生類對象存放在使用基類實例化的容器中的剝離(slicing)問題。

在考慮容器中是存放對象還是指針的時候腦子裏時刻要想到,我的操作需要容器做多少拷貝工作,這些拷貝操作帶來的損耗能否接受,從這個本質問題上把握好了,選擇起來就不是問題了,要根據實際情況靈活運用。

原文鏈接:http://blog.csdn.net/love_hot_girl/article/details/7404178


轉自鏈接:http://www.oschina.net/question/234345_55497
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章