2017-09-04 169 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”一个应该预留的大小(如果它是事先已知的),以避免频繁的重新分配。

相关问题