析构函数调用的次数比我预期的多

Destructor called one more time than I expected

这是我的最小例子:

#include <iostream>
#include <vector>

class A {
public:
    A() { std::cout << "Constructor\n"; }
    ~A() { std::cout << "Destructor\n"; }
};

class B {
public:
    B() { v.push_back(A()); v.push_back(A()); }
private:
    std::vector<A> v;
};

int main() {
    B b;
    return 0;
}

所以,我得到了这个输出:

Constructor   // first push back
Destructor    // copy destroyed
Constructor   // second push back
Destructor    // copy destroyed
Destructor    // ???????????????
// B object goes out of scope and its vector too...
Destructor    // v[0] destructor called 
Destructor    // v[1] destructor called 

有人可以解释一下吗?


关注 Dave 的评论后:

Constructor
Copy constructor
Destructor
Constructor
Copy constructor
Copy constructor
Destructor
Destructor
Destructor
Destructor

您没有使用默认复制构造函数跟踪 A 的构造。如果在其中添加复制构造函数和消息,则构造函数的调用次数应与析构函数的调用次数匹配。

添加一个重载的复制构造函数和一些正在对哪个对象进行操作的指示器可以阐明这种情况:

#include <iostream>
#include <vector>

class A {
public:
    A(unsigned i): i(i) { std::cout << "Constructor " << i << std::endl; }
    A(const A& a) : i(a.i) { std::cout << "Copy constructor " << i << std::endl; }
    ~A() { std::cout << "Destructor " << i << std::endl; }
    unsigned i;
};

class B {
public:
    B() { v.push_back(A(0)); v.push_back(A(1)); }
private:
    std::vector<A> v;
};

int main() {
    B b;
    return 0;
}

在第一次推送时,我们复制并销毁临时文件。在第二次推送时,我们制作一个副本,然后复制第一个对象,然后销毁第一个对象和临时对象。最后,我们销毁这两个对象。

我猜想 std::vector 第一次分配容量为 1,所以第二次推送强制重新分配?如果我强制使用更大的初始容量(通过在第一次推送之前调用 v.reserve(5)),那么额外的副本就会消失。

修改classA的声明如下,

class A 
{
public:
  A() { std::cout << "Constructor " << this << std::endl; }
  A(const A&) { std::cout << "Copy Constructor " << this << std::endl; }
  ~A() { std::cout << "Destructor " << this << std::endl; }
};

运行 http://coliru.stacked-crooked.com/ 的节目 给出输出:

构造函数 0x7fffecb8dc0f

复制构造函数 0x1efdc20

析构函数 0x7fffecb8dc0f

构造函数 0x7fffecb8dc0e

复制构造函数 0x1efdc41

复制构造函数 0x1efdc40

析构函数 0x1efdc20

析构函数 0x7fffecb8dc0e

析构函数 0x1efdc40

析构函数 0x1efdc41

清楚地显示了堆栈和堆上对象的构造和销毁(0x1efdc20、0x1efdc40 和 0x1efdc41 是 vector 分配的对象的位置)。