2014-11-04 60 views
4

我有點困惑與矢量push_back行爲的方式,以下代碼片段我期望複製構造函數被調用只有兩次,但輸出建議否則。這是一種導致這種行爲的矢量內部重構嗎?不止一次向量push_back調用copy_constructor?

輸出:

Inside default 

Inside copy with my_int = 0 

Inside copy with my_int = 0 

Inside copy with my_int = 1 
class Myint 
{ 
private: 
    int my_int; 
public: 
    Myint() : my_int(0) 
    { 
     cout << "Inside default " << endl; 
    } 
    Myint(const Myint& x) : my_int(x.my_int) 
    { 
     cout << "Inside copy with my_int = " << x.my_int << endl; 
    } 

    void set(const int &x) 
    { 
     my_int = x; 
    } 
} 

vector<Myint> myints; 
Myint x; 

myints.push_back(x); 
x.set(1); 
myints.push_back(x); 
+0

「emplace_back」的相關問題:http://stackoverflow.com/questions/23948442/emplace-back-does-not-behave-as-expected/23948499?s=8|0.0000#23948499 – juanchopanza 2014-11-04 17:25:12

回答

8

會發生什麼:

  1. x通過push_back插入。發生一個副本:新創建的元素用參數初始化。 my_int被接管爲零,因爲x的默認構造函數將其初始化。

  2. 第二個元素是push_back'd;該向量需要重新分配內存,因爲已經達到了the internal capacity
    由於沒有移動構造函數被隱式定義爲Myint 選擇了複製構造函數;第一個元素被複制到新分配的內存中(其my_int仍爲零...因此複製構造函數再次顯示my_int0),然後複製x以初始化第二個元素(與第一步中的第一個元素一樣)。這一次x已將my_int設置爲1,這就是複製構造函數的輸出告訴我們的內容。

所以通話總數是三。由於初始容量可能不同,因此從一個實施到另一個可能會有所不同。但是,兩個電話是最低限度的。

可以通過減少複印量,提前,保留更多的內存 - 即,較高的載體能力,以便重新分配變得不必要:

myints.reserve(2); // Now two elements can be inserted without reallocation. 

此外可以的Elid副本插入如下時:

myints.emplace_back(0); 

這種「emplaces」一個新的元素 - emplace_back是一個可變參數模板,因此可以採取的參數,它然後轉發任意量 - 不復制或移動 - 的元素構造。

因爲有一個用戶聲明的拷貝構造函數。

3

當向量的大小與第二push_back增加,載體的現有內容必須被複制到一個新的緩衝區。要驗證,輸出myints.capacity()後第一個push_back,應該是1

+0

無關:爲什麼[同樣的例子](http://coliru.stacked-crooked.com/a/e6284e04a0389e29)在OP代碼中輸出'0 1 0'而不是'0 0 1'? – 0x499602D2 2014-11-04 16:57:44

+0

@ 0x499602D2實現可能會在複製舊緩衝區之前將'push_back'元素複製到新緩衝區。 – 2014-11-04 16:59:44

3

這取決於有多少內存保留std::vector類型的對象。看起來,當第一次執行push_back時,僅爲一個元素分配了內存。當第二次調用push_back時,內存被重新分配以爲第二個元素保留內存。在這種情況下,已經在矢量中的元素被複制到新的位置。然後第二個元素也被添加。

你可以預留足夠的內存自己,以躲避拷貝構造函數的第二個電話:

vector<Myint> myints; 
myints.reserve(2); 
3

你知道了......這是調整大小。但我只想指出,如果你做你的構造一些豆計數,你可能會感興趣的「炮臺」:

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

class Myint 
{ 
private: 
    int my_int; 
public: 
    explicit Myint(int value = 0) : my_int(value) 
    { 
     cout << "Inside default " << endl; 
    } 
    Myint(const Myint& x) : my_int(x.my_int) 
    { 
     cout << "Inside copy with my_int = " << x.my_int << endl; 
    } 
    Myint(const Myint&& x) noexcept : my_int(x.my_int) { 
     cout << "Inside move with my_int = " << x.my_int << endl; 
    } 
}; 

int main() { 
    vector<Myint> myints; 
    myints.reserve(2); 

    myints.emplace_back(0); 
    myints.emplace_back(1); 

    // your code goes here 
    return 0; 
} 

這應該給你:

Inside default 
Inside default 

而且,由於noexcept on the move constructor ...如果你刪除reserve你會得到一招,而不是一個副本:

Inside default 
Inside default 
Inside move with my_int = 0 

有沒有真正的優勢,這種數據類型的搬過來複印件。但從語義上講,如果你的數據類型更加「重量級」並且有一種「移動」其成員的方式,這更像是將某個指針的所有權轉移給大型數據結構,那麼它可能會有很大的差異。