为什么vector hold一个class类型会在push_back()时再调用一次拷贝构造函数?

Why vector hold a class type will call the copy constructor one more time when 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()到一个vector时只是压入了两个元素。但是为什么上面的结果显示调用了3次
但是,根据main中的for循环,vector的元素是对的。

为什么复制构造函数被压入一个vector时又被调用了一次?我定义的复制构造函数有什么问题吗?

第二次调用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

根据C++ language open standard draft n3690.pdf 关于向量容量。请参阅粗体斜体声明。

23.3.7.3 vector capacity [vector.capacity] size_type capacity() const noexcept; 1 Returns: The total number of elements that the vector can hold without requiring reallocation. void reserve(size_type n); 2 Requires: T shall be MoveInsertable into *this. 3 Effects: A directive that informs a vector of a planned change in size, so that it can manage the storage allocation accordingly. After reserve(), capacity() is greater or equal to the argument of reserve if reallocation happens; and equal to the previous value of capacity() otherwise. "Reallocation happens at this point if and only if the current capacity is less than the argument of reserve()". If an exception is thrown other than by the move constructor of a non-CopyInsertable type, there are no effects

也来自 Scott Meyers“Effective C++ Digital Collection: 140 Ways to Improve Your Programming”,在项目 14 下 项目 14。

Use reserve to avoid unnecessary reallocations. One of the most marvelous things about STL containers is that they automatically grow to accommodate as much data as you put into them, provided only that you don't exceed their maximum size. (To discover this maximum, just call the aptly named max_size member function.) For vector and string, growth is handled by doing the moral equivalent of a realloc whenever more space is needed. This realloc-like operation has four parts: 1. Allocate a new block of memory that is some multiple of the container's current capacity. In most implementations, vector and string capacities grow by a factor of between 1.5 and 2 each time.

根据“songyuanyao”的建议,应该保留大小(如果事先知道)以避免频繁重新分配。