为什么我的析构函数被调用的次数比预期的要多

Why are my destructors being called a greater than number of times than expected?

本文关键字:我的 析构函数 调用 为什么      更新时间:2023-10-16

我写这段代码是为了检查c++中析构函数的行为

#include <vector>
#include <iostream>
using namespace std;

class WrongDestructor
{
private:
int number;
public:
WrongDestructor(int number_) :
number(number_)
{}
~WrongDestructor() {
cout<<"Destructor of " <<number<<endl;
//  throw int();
}
};
int main(int argc, char *argv[])
{
std::vector<WrongDestructor> wrongs;
for(int i = 0; i < 10; ++i) {
wrongs.push_back(WrongDestructor(i));
}
return 0;
}

我发现有趣的是我的程序的输出:

Destructor of 0
Destructor of 0
Destructor of 1
Destructor of 0
Destructor of 1
Destructor of 2
Destructor of 3
Destructor of 0
Destructor of 1
Destructor of 2
Destructor of 3
Destructor of 4
Destructor of 5
Destructor of 6
Destructor of 7
Destructor of 0
Destructor of 1
Destructor of 2
Destructor of 3
Destructor of 4
Destructor of 5
Destructor of 6
Destructor of 7
Destructor of 8
Destructor of 9
Destructor of 0
Destructor of 1
Destructor of 2
Destructor of 3
Destructor of 4
Destructor of 5
Destructor of 6
Destructor of 7
Destructor of 8
Destructor of 9

这意味着创建的对象比我想象的要多得多。当我在for循环中填充集合时,我预计集合中显然会有10个,接下来的10个可能会作为临时对象创建。但它们还有更多,其中一些甚至比其他的更经常被创造。

vector必须分配一个更大的内存块来容纳元素时,新元素会被移动到新的、更大的存储块中。因为您的类型没有定义移动构造函数或复制构造函数,所以您将获得编译器提供的默认复制构造函数。默认的复制构造函数对类中的所有成员执行简单的成员复制。

此外,使用push_back插入元素本身需要将其移动或复制到vector中。因此,假设编译器没有对此进行优化,您也会在那里获得副本。(请注意,您可以使用emplace_back来避免这些副本。)

因此,您会提前将实例的多个副本插入到容器中,因为当例如1被复制到vector内部的较大内存缓冲区时,它会在旧的较小缓冲区中销毁。

通过定义复制和/或移动构造函数,您可以更清楚地看到这种行为:

#include <vector>
#include <iostream>
using namespace std;

class WrongDestructor
{
private:
int number;
public:
WrongDestructor(int number_) :
number(number_)
{}
// Copy constructor
WrongDestructor(WrongDestructor const& copied)
: number(copied.number)
{
cout << "Copied " << this->number << endl;
}
~WrongDestructor() {
cout<<"Destructor of " <<number<<endl;
//  throw int();
}
};
int main(int argc, char *argv[])
{
std::vector<WrongDestructor> wrongs;
for(int i = 0; i < 10; ++i) {
wrongs.push_back(WrongDestructor(i));
}
return 0;
}

该程序提供以下输出:http://ideone.com/S5Zf41

您的向量没有预先确定的大小。当您将对象推回到向量上时,它必须重新分配向量本身以容纳新条目。这意味着将对象从内存缓冲区复制到新的缓冲区中。埃尔戈,当它这样做的时候,你会看到更多的副本。

您可以使用std::vector::reserve为向量分配足够的空间。否则,向量需要为传入元素重新分配连续空间,这会导致大量副本。

std::vector<WrongDestructor> wrongs;
wrongs.reserve(10);

是的,看起来你在向量中添加了10个对象,但实际上又创建了10个,所以最后还会销毁10个对象。

你应该知道,当向量的内容越来越多时,向量会增加它容纳更多对象的容量,在这个过程中发生了什么?

如果vector没有空间容纳新对象,它将为更多对象创建一个更大的内存块,并复制它所拥有的对象。然后添加新的对象(通过push_back),它将销毁对象并释放满足它们的原始内存。

因此,当复制更多创建的对象时,当销毁原始对象时,将调用更多的析构函数。

最好的方法是提供复制构造函数,并在其中打印一些内容。并且在ctor中打印一些东西,你会看到整个过程。

你需要知道的更多的是向量的容量和储备。