为什么我的析构函数被调用的次数超过预期?

时间:2012-12-09 08:28:22

标签: c++ destructor

我编写此代码来检查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

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

4 个答案:

答案 0 :(得分:4)

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

答案 1 :(得分:3)

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

答案 2 :(得分:1)

您可以使用std::vector::reserve为矢量分配足够的空间。否则,vector需要为传入元素重新分配连续空间,从而产生大量副本。

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

答案 3 :(得分:1)

是的,看起来您在向量中添加了10个对象,但实际上还创建了10个对象,因此最后会有10个对象被销毁。

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

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

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

最好的策略是你提供copy-constructor,并在其中提供printf内容。 并且还会在ctor中打印一些内容,您将看到整个过程。

您需要知道的是矢量的容量和储备。