【问题标题】:Why is my destructor getting called multiple times?为什么我的析构函数被多次调用?
【发布时间】:2019-02-09 16:08:47
【问题描述】:

我创建了一个类并创建了这个类的向量。我在析构函数中放了一条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;
}

控制台输出:

count: 0
destructor called. ID: 0
count: 1
destructor called. ID: 0
destructor called. ID: 1
count: 2
destructor called. ID: 0
destructor called. ID: 1
destructor called. ID: 2
count: 3
destructor called. ID: 0
destructor called. ID: 1
destructor called. ID: 2
destructor called. ID: 3
count: 4
destructor called. ID: 0
destructor called. ID: 1
destructor called. ID: 2
destructor called. ID: 3
destructor called. ID: 4
destructor called. ID: 0
destructor called. ID: 1
destructor called. ID: 2
destructor called. ID: 3
destructor called. ID: 4

【问题讨论】:

  • 尝试一个比total_count = 5;更大的数字,你会发现每次push_back都不会发生重新分配,并且随着大小的增加它变得不那么常见(这是应该的是)。
  • 顺便说一句,我建议你使用myvec.reserve(5)myvec.emplace_back(count)。那么所有的析构函数在myvec.clear()之前都是removed
  • 在您的析构函数中,您打印的信息并未显示正在发生的事情。如果您在析构函数中打印了this 的值而不是_id(并且还在构造函数中打印了this),您会看到this 引用了不同的对象。那个不同的对象是什么——这就是你可以制定出正在发生的事情的地方,然后问“这个不同的对象来自哪里?”而不是“为什么我的析构函数被多次调用?”。
  • 将日志记录添加到类的转换和复制构造函数中,这将使您更好地了解正在发生的事情。 temp(int id) : _id(id) { cerr &lt;&lt; "converting constructor called. " &lt;&lt; this &lt;&lt; " ID: " &lt;&lt; _id &lt;&lt; endl; } temp(const temp &amp;src) : _id(src._id) { cerr &lt;&lt; "copy constructor called. " &lt;&lt; this &lt;&lt; " ID: " &lt;&lt; _id &lt;&lt; endl; } ~temp() { cerr &lt;&lt; "destructor called. " &lt;&lt; this &lt;&lt; " ID: " &lt;&lt; _id &lt;&lt; endl; }

标签: c++ memory-management heap-memory


【解决方案1】:

由于每次std::vector 调整大小时都完成了复制,所以调用了析构函数。

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

如果您事先知道该向量需要保存多少个实例,您可以reserve 它达到这个数量。

【讨论】:

    【解决方案2】:

    让我们看看

    myvec.push_back(temp(count));
    

    在这里,您使用temp(count) 创建一个临时的temp 对象。然后将其作为 copy 存储在向量中。然后临时对象就被销毁了。

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

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

    至于向量的大小调整算法是如何工作的,它是非常特定于实现的,但一种常见的方法是在大小较小时为每个push_back 调整大小,然后随着大小的增加保留越来越大的块。

    如果您不希望这种调整大小和复制,那么只要您知道要存储在向量中的元素数量,您就可以设置特定的大小开始并使用正常的数组索引语法分配给元素,或者您可以在前面reserve 空格。

    【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-01-12
    • 2021-11-06
    • 2010-12-21
    • 2013-12-11
    • 2013-11-24
    • 1970-01-01
    相关资源
    最近更新 更多