STL容器裡存放物件還是指標

love_hot_girl發表於2020-04-07
STL容器裡存放物件還是指標
2011-10-12 11:00

轉載自:http://hsw625728.blog.163.com/blog/static/3957072820091116115732821/

一.問題的引出:

容器可以存放物件,可以存放指標,這裡要談的是兩者的使用問題。就是什麼時候存放物件更好,什麼時候存放指標更好?

 

二.問題的分析過程:

1. 首先說下stl容器的工作方式

 

對於內建型別(int float char),容器的工作方式是純粹的位拷貝,這裡沒有什麼需要多說的。

對於自定義的物件,容器容納了物件(比如通過insertpush_back等),但容器中存放的物件不是你給它們的那個物件,因為兩個物件在記憶體中的位置不一樣。此外,當你從容器中獲取一個物件時,你所得到的物件不是容器裡的那個物件。取而代之的是,當你向容器中新增一個物件(比如通過insertpush_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;

}

輸出如下:

 

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

MAX = 40000

MAX = 400000

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

 

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

 

2. 存放指標的情況

 

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

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

三.問題的總結:

通過上面的分析,總結了一下幾點:

1.       Stl容器可以存放內建型別、自定義型別、指標型別的元素。

2.       元素如果是內建資料型別,那麼就存放資料本身。

3.       元素如果是複雜型別,並且在使用容器的過程中需要容器的元素進行大量的拷貝操作的時候,就要考慮在容器中放入指標;

4.       存放指標容易出現記憶體的洩露,所以在使用的時候需要考慮清楚,如能介面設計的合理,能保證容器在使用的過程中不進行大量的拷貝工作,在容器中存放物件是最好的了。

5.       使用智慧指標是一種兩種優點都兼備的,既有指標的操作效率,又避免了自己手動管理記憶體帶來的問題。

6.       指標可以解決派生類物件存放在使用基類例項化的容器中的剝離(slicing)問題。

 

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

相關文章