2011-05-31 67 views
4

我寫以下代碼:C++:添加自動分配對象到一個std ::矢量

#include <iostream> 
#include <vector> 
using namespace std; 

class AClass 
{ 
    public: 
     int data; 

     AClass() 
     { data = -333; cout << "+ Creating default " << data << endl; } 

     AClass(const AClass &copy) 
     { data = copy.data; cout << "+ Creating copy of " << data << endl; } 

     AClass(int d) 
     { data = d; cout << "+ Creating " << data << endl; } 

     ~AClass() 
     { cout << "- Deleting " << data << endl; } 

     AClass& operator = (const AClass &a) 
     { data = a.data; cout << "= Calling operator=" << endl; } 
}; 

int main(void) 
{ 
    vector<AClass> v; 

    for (int i = 3; i--;) 
     v.push_back(AClass(i)); 

    vector<AClass>::iterator it = v.begin(); 
    while (it != v.end()) 
     cout << it->data << endl, it++; 

    return 0; 
} 

並從該程序的輸出結果是:

+ Creating 2 
+ Creating copy of 2 
- Deleting 2 
+ Creating 1 
+ Creating copy of 1 
+ Creating copy of 2 
- Deleting 2 
- Deleting 1 
+ Creating 0 
+ Creating copy of 0 
+ Creating copy of 2 
+ Creating copy of 1 
- Deleting 2 
- Deleting 1 
- Deleting 0 
2 
1 
0 
- Deleting 2 
- Deleting 1 
- Deleting 0 

然後,我改變的類:

class AClass 
{ 
    public: 
     int data; 

     AClass(int d) 
     { data = d; cout << "+ Creating " << data << endl; } 

     ~AClass() 
     { cout << "- Deleting " << data << endl; } 
}; 

,輸出變爲:

+ Creating 2 
- Deleting 2 
+ Creating 1 
- Deleting 2 
- Deleting 1 
+ Creating 0 
- Deleting 2 
- Deleting 1 
- Deleting 0 
2 
1 
0 
- Deleting 2 
- Deleting 1 
- Deleting 0 

看起來,矢量在添加新的對象時正在製作現有對象的副本,但似乎有很多不必要的分配/刪除正在發生。爲什麼是這樣?另外,爲什麼第二個版本在我沒有提供拷貝構造函數時工作?

回答

5

看來,vector時,正在製作現有對象的副本添加了一些元素

當您添加元素時,例如與v.push_back(AClass(i));,做什麼是臨時的AClass對象被創建並傳遞到push_backpush_back必須將此對象複製到容器中。

您看到副本的另一個原因是std::vector將其元素連續存儲在一個數組中。如果底層數組中沒有剩餘空間,並且您嘗試在末尾添加另一個元素,則std::vector必須創建一個新數組,將舊數組中的元素複製到新數組中,然後將新元素插入到末尾。如果您不希望發生這種情況,可以在開始插入元素之前調用std::vector::reserve以在std::vector中保留足夠的空間,也可以使用不同的序列容器,如std::deque,該容器不會連續存儲其元素。

好像很多不必要的分配的/缺失正在發生

在C++程序,經常創建和銷燬對象。請注意,在您的程序中,AClass複製起來非常便宜:它的大小可能是四個或八個字節,只是大到足以容納其數據成員int

如果你有一個昂貴的類型複製(例如,也許你有一個大型的數據結構,有成千上萬的節點),那麼是的,複製可能太昂貴。在這種情況下,您可以將智能指針存儲到std::vector中的動態分配對象中(例如,std::vector<shared_ptr<AClass> >)。如果您的編譯器支持右值引用並且具有移動感知標準庫實現,則可以通過實現移動構造函數和移動賦值運算符並使用emplace_back而不是push_back來製作昂貴的複製類型。

爲什麼第二個版本在我沒有提供複製構造函數的情況下工作?

如果您沒有聲明覆制構造函數,編譯器會爲您提供默認的複製構造函數。

+0

謝謝!這有很大的幫助 - 你的回答解決了問題的每一部分。 – milesleft 2011-05-31 22:43:20

3

您的對象被複制,因爲矢量正在擴展其內部存儲。如果您想避免複製,請事先撥打vector::reserve預先分配內存。如果你沒有提供你自己的拷貝文件,編譯器會爲你生成一個拷貝(一個拷貝所有成員)。

+0

我認爲一旦你聲明瞭任何非默認的構造函數,編譯器就希望你提供所有必要的ctors。我猜這是錯誤的!謝謝你清理那個。 – milesleft 2011-05-31 22:50:26

+0

@milesleft:如果你爲你的類聲明瞭任何構造函數,編譯器將不會自動生成默認的構造函數。但是,它會自動生成一個拷貝構造函數,除非你聲明你自己的拷貝構造函數。 – 2011-06-01 00:40:41

0

首先拷貝cunstructor isusualy由C++自己生成,如果你沒有提供,那麼vector就是拷貝所有的數據,因爲它不確定你給它的變量是否有有效的值,例如你需要它的時候你可以使用一些局部變量並將它傳遞給向量,如果向量不復制你給它的東西,並且你返回那個向量,那麼會有一些內存違規。無論你添加一些新的對象,它需要一個更大的數組來存儲它分配一個新數組的所有對象,然後將所有現有數據複製到新數組中。

+0

謝謝Gajet !!! – milesleft 2011-05-31 22:44:33

5

Vector使用T的常規數組作爲它的存儲 - 當您創建其中一個時,它必須以某種方式初始化空間,唯一的選項是默認構造函數。稍後,當您設置索引的值時,會將其複製到該空間。

在第二個版本中,即使您不提供複製構造函數,也會自動爲您生成一個。如果你聲明一個私人的,然後不執行它,你會看到一個編譯器錯誤(因爲你已經禁止了默認的代)

+0

謝謝!這有很大幫助。 – milesleft 2011-05-31 22:42:59