C++ 为什么我的析构函数被多次调用?

C++ 为什么我的析构函数被多次调用?,c++,memory-management,heap-memory,C++,Memory Management,Heap Memory,我已经创建了一个类,并创建了这个类的向量。我在析构函数中放入了一条cerr消息,以查看何时调用它。我发现同一个析构函数被调用了不止一次。这让我感到困惑 #include <iostream> #include <vector> using namespace std; class temp { private: int _size = 1000; int _myBall [1000]; int _id; public: temp(int

我已经创建了一个类,并创建了这个类的向量。我在析构函数中放入了一条
cerr
消息,以查看何时调用它。我发现同一个析构函数被调用了不止一次。这让我感到困惑

#include <iostream>
#include <vector>

using namespace std;

class temp {
private:
    int _size = 1000;
    int _myBall [1000];
    int _id;
public:
    temp(int id) : _id(id) {}
    ~temp() {
        cerr << "destructor called. ID: " << _id << endl;
    }
};

int main() 
{
    vector<temp> myvec;
    int total_count = 5;
    int count = total_count;
    for(int count = 0;count < total_count; count++) {
        cerr << "count: " << count << endl;
        myvec.push_back(temp(count));
    }

    myvec.clear();
    cerr << "Hello World" << endl;
    system("pause");
    return 0;
}

由于每次调整
std::vector
大小时都会进行复制,因此会调用析构函数

std::vector
在构建后重新分配预先确定的内存量(足以容纳一定数量的
temp
实例)-这就是
容量。每次调用
push_back
时,它都会评估是否仍有足够的内存分配给新实例。一旦它被填满,它实际上会重新分配另一块内存(足以分配更多的
temp
实例),然后复制(或移动,如果可能的话)所有现有实例。这些是您看到的已记录的析构函数调用


如果您事先知道向量需要保存多少个实例,您可以
将其保留到该数量。

由于每次调整
std::vector
大小时都会进行复制,因此会调用析构函数

std::vector
在构建后重新分配预先确定的内存量(足以容纳一定数量的
temp
实例)-这就是
容量。每次调用
push_back
时,它都会评估是否仍有足够的内存分配给新实例。一旦它被填满,它实际上会重新分配另一块内存(足以分配更多的
temp
实例),然后复制(或移动,如果可能的话)所有现有实例。这些是您看到的已记录的析构函数调用

如果您事先知道向量需要保存多少实例,您可以
将其保留到该数量。

让我们看看

myvec.push_back(temp(count));
在这里,您可以使用
temp(count)
创建一个临时
temp
对象。然后将其作为副本存储在向量中。然后临时对象被销毁

临时对象的销毁是调用析构函数的一种情况

然后,当向量动态调整自身大小时,它将内容复制到新的更大的数据内存中。然后,较小数据内存中的对象将被销毁。这当然会导致调用析构函数。这种调整大小和复制可能会发生多次

至于向量的大小调整算法是如何工作的,这是非常具体的实现,但一种常见的方法是在大小较小时为每个
push\u back
调整大小,然后随着大小的增加保留越来越大的块

如果您不想进行这种大小调整和复制,那么只要知道要存储在向量中的元素数量,就可以设置开始时的特定大小,并使用常规数组索引语法分配给元素,或者可以预先留出空间。

让我们看一看

myvec.push_back(temp(count));
在这里,您可以使用
temp(count)
创建一个临时
temp
对象。然后将其作为副本存储在向量中。然后临时对象被销毁

临时对象的销毁是调用析构函数的一种情况

然后,当向量动态调整自身大小时,它将内容复制到新的更大的数据内存中。然后,较小数据内存中的对象将被销毁。这当然会导致调用析构函数。这种调整大小和复制可能会发生多次

至于向量的大小调整算法是如何工作的,这是非常具体的实现,但一种常见的方法是在大小较小时为每个
push\u back
调整大小,然后随着大小的增加保留越来越大的块


如果不希望进行这种调整大小和复制,那么只要知道要存储在向量中的元素数量,就可以设置开始时的特定大小,并使用常规数组索引语法分配给元素,或者可以预先留出空间。

例如,GCC似乎在每次需要时分配两倍于以前的容量:MSVC也这样做例如,GCC似乎在每次需要时分配两倍于以前的容量:MSVC也这样做尝试一个大于
total_count=5的数字
,你会发现重新分配并不是每次你推回时都会发生,而且随着大小的增加(这是应该的)。顺便说一句,我建议你使用
myvec.reserve(5)
myvec.emplace\u back(count)
。然后,所有析构函数都将位于myvec.clear()之前。
。在析构函数中,您打印的信息不会显示正在发生的事情。如果在析构函数中打印
this
的值而不是
\u id
(并且在构造函数中打印
this
),您会看到
this
引用了不同的对象。这个不同的对象是什么——在这里,您可以对正在发生的事情进行公式化,并询问“这个不同的对象从何而来?”而不是“为什么我的析构函数被多次调用?”。将日志记录添加到类的转换和复制构造函数中,这将使您更好地了解正在发生的事情
temp(int-id):\u-id(id){cerr尝试一个大于
total\u count=5;
的数字,你会发现重新分配不会在每次你
push\u back
时发生,并且随着大小的增加(这是应该的)。顺便说一句,我建议你使用
myvec.reserve(5)
myvec.emplace\u back(count)
。然后所有析构函数都将位于
myvec.clear()
之前。在析构函数中,您正在打印的信息没有显示正在发生的事情。如果您想在y中打印
this
的值而不是
\u id