2017-09-04 177 views
2

我有以下代碼:爲什麼vector持有一個類的類型會再次調用複製構造函數push_back()?

#include <iostream> 
using std::cin; using std::cout; using std::endl; 
#include <vector> 
using std::vector; 

class Quote { 
public: 
    Quote() = default; 
    Quote(const std::string &book, double sales_price): 
        bookNo(book), price(sales_price) { } 
    // Quote(const Quote&) = default; // memberwise copy 
    Quote(const Quote &orig): bookNo(orig.bookNo), price(orig.price) { 
     cout << orig.isbn() << endl; 
     cout << "called Quote(const Quote &)" << endl; 
    } 
    Quote& operator=(const Quote&) = default; // copy assign 

    std::string isbn() const { return bookNo; } 
    virtual double net_price(std::size_t n) const 
       { cout << "Quote::net_price\n"; return n * price; } 
    virtual void debug() const { cout << bookNo << ' ' << price << endl; } 
    virtual ~Quote() = default; 
private: 
    std::string bookNo; // ISBN number of this item 
protected: 
    double price = 0.0; // normal, undiscouted price 
}; 

int main(int argc, char *argv[]) { 
    vector<Quote> basket; 
    basket.push_back(Quote("0-201-82470-1", 50)); 
    basket.push_back(Quote("0-201-82XXXXX", 30)); 
    cout << "\ntraverse bakset" << endl; 
    for (const auto &v : basket) 
     v.debug(); 
} 

後,我編譯上面的代碼並運行,其結果是:

0-201-82470-1 
called Quote(const Quote &) 
0-201-82XXXXX 
called Quote(const Quote &) 
0-201-82470-1 
called Quote(const Quote &) 

traverse bakset 
0-201-82470-1 50 
0-201-82XXXXX 30 

據時拷貝構造函數被調用時,它會被調用兩次因爲當我將push_back()添加到矢量時,我只是推了兩個元素。但是爲什麼在上面的結果中顯示三次調用
但是,根據main中的for循環,向量的元素是正確的。

爲什麼複製構造函數被再次調用時被推到一個向量?我的定義的複製構造函數有什麼問題嗎?

+1

當矢量空間不足時會發生什麼? – tkausl

+0

@tkausl,對不起。我沒有遇到過這種情況,即矢量空間不足。 – zhenguoli

+0

@tkausl。謝謝。我可能理解你的意思。 – zhenguoli

回答

2

第二次調用push_back時,發生重新分配。 (更確切地說,當新的size()大於capacity()時會發生這種情況。)然後vector的舊底層存儲將被銷燬,並且新的存儲將被分配,並且元素需要被複制到新的存儲中,這會導致複製構造函數被稱爲。

您可以使用reserve來避免重新分配。例如

vector<Quote> basket; 
basket.reserve(2); 
basket.push_back(Quote("0-201-82470-1", 50)); 
basket.push_back(Quote("0-201-82XXXXX", 30)); // no reallocation here 
+0

你可以使用emplace_back來避免所有'basket.emplace_back(「0-201-82470-1」,50);' –

+0

@ArtemyVysotsky複製構造函數當矢量重新分配發生時不會避免複製。避免該副本的正確方法是實現移動構造函數(編譯器可以自動執行,但由於用戶定義的複製構造函數和複製賦值運算符而不會在這裏)。 – cdhowie

+0

我的意思是使用emplace_back除了保留。移動構造函數必須在調整大小時不被矢量使用。演示,顯示push_back和emplace之間的區別可以在這裏看到https://gist.github.com/artemyv/6d07927980bf8d8f2536354cc8bc5c98 - 這是回答ro類似的問題https://stackoverflow.com/questions/45893180/how-to-explain - 即,有-是-9-倍-的毀滅 –

0

作爲關於每向量容量C++ language open standard draft n3690.pdf 。請看大膽的斜體聲明。

23.3.7.3向量容量[vector.capacity] size_type capacity()const noexcept; 1返回:無需重新分配,矢量可以容納的元素總數。 void reserve(size_type n); 2要求:T應該是MoveInsertable into * this。 3影響:一個指令,通知矢量大小的計劃更改,以便它可以相應地管理存儲分配。在reserve()之後,如果 重新分配發生,capacity()大於或等於reserve的參數;並等於先前的capacity()的值,否則。 「重新分配發生 此時當且僅當當前容量小於參數reserve()」。如果一個異常 比由非CopyInsertable類型的移動構造函數拋出的,有沒有影響

從斯科特邁爾斯

而且「有效的C++數字館藏:140的方法來提高你的編程」,下item 14 Item 14.

使用保留來避免不必要的重新分配。 STL容器最令人驚歎的地方之一就是它們會自動增長以適應放入它們的儘可能多的數據,只要你不超過它們的最大尺寸。 (要發現這個最大值,只需調用恰當命名的max_size成員函數。)對於向量和字符串,只要需要更多空間,就會通過執行realloc的道德等價來處理增長。這種類似realloc的操作包含四個部分: 1.分配一個新的內存塊,它是容器當前容量的幾倍。 在大多數實現中,矢量和字符串容量每增加,就會以1.5到2之間的係數增長。

如所建議的「songyuanyao」一個應該預留的大小(如果它是事先已知的),以避免頻繁的重新分配。

相關問題