奇怪的毁灭对象

时间:2015-05-23 14:12:28

标签: c++ destructor

我很困惑为什么析构函数被调用了很多次。

#include <iostream>
#include <vector>

class Box
{
    public:
        int x;
        Box(int x);
        ~Box();
};

Box::~Box()
{
    std::cout << x << " Destroyed\n";
}

Box::Box(int x)
{
    this->x = x;
    std::cout << x << " Created\n";
}

int main()
{
    std::vector<Box> boxList;

    for (int i = 0; i < 3; i++)
    {
        Box b(i);
        boxList.push_back(b);
    }

    return 0;
}

输出:

0 Created
0 Destroyed
1 Created
0 Destroyed
1 Destroyed
2 Created
0 Destroyed
1 Destroyed
2 Destroyed

主要退出后将打印出来。我在析构函数中保留了一个getchar()来停止程序执行。否则我们不会看到这些线被打印出来。

0 Destroyed
1 Destroyed
2 Destroyed

有人可以解释一下。

5 个答案:

答案 0 :(得分:1)

其他答案正确地提到了隐式copy constructor的调用。要查看这些调用,只需使用显式复制构造函数替换隐式复制构造函数:

#include <iostream>
#include <vector>

class Box
{
    public:
        int x;
        int copy_nr;
        Box(int x);
        Box(const Box& other); //copy constructor
        ~Box();
};

Box::~Box()
{
    std::cout << x << " (copy " << copy_nr << ") Destroyed" << std::endl;
}

Box::Box(int x) : x(x), copy_nr(0)
{
    std::cout << x << " (copy " << copy_nr << ") Created" << std::endl;
}

Box::Box(const Box& other) : x( other.x ), copy_nr( other.copy_nr + 1 )
{
        std::cout << x << " (copy " << other.copy_nr <<") Copied"
                     " (creating copy " << copy_nr << ")" << std::endl;
}

int main()
{
    std::vector<Box> boxList;

    for (int i = 0; i < 3; i++)
    {
        Box b(i);
        boxList.push_back(b);
    }

    return 0;
}

在我的机器上,这会产生:

0 (copy 0) Created
0 (copy 0) Copied (creating copy 1)
0 (copy 0) Destroyed
1 (copy 0) Created
1 (copy 0) Copied (creating copy 1)
0 (copy 1) Copied (creating copy 2)
0 (copy 1) Destroyed
1 (copy 0) Destroyed
2 (copy 0) Created
2 (copy 0) Copied (creating copy 1)
0 (copy 2) Copied (creating copy 3)
1 (copy 1) Copied (creating copy 2)
0 (copy 2) Destroyed
1 (copy 1) Destroyed
2 (copy 0) Destroyed
0 (copy 3) Destroyed
1 (copy 2) Destroyed
2 (copy 1) Destroyed

其他计算机上的结果可能不同,因为std::vector的实现可以决定何时重新分配其内部数组。

答案 1 :(得分:0)

这有两个原因。

首先,push_back有时会扩展向量的大小,重新分配空间,复制所有对象,并销毁旧对象。要摆脱这种重新分配的工件,可以在循环之前添加boxList.reserve(3),并获得更易理解的输出:

0 Created
0 Destroyed
1 Created
1 Destroyed
2 Created
2 Destroyed
0 Destroyed
1 Destroyed
2 Destroyed

其次,push_back制作本地b对象的副本,然后销毁此b。这解释了为什么上面输出中的每个对象在创建后很快就会被销毁。

最后三个电话当然是在节目退出时。

答案 2 :(得分:0)

  

0创建

创建本地Box b(0),复制到vector

  

0被摧毁

并销毁

  

1创建

现在创建了本地Box b(1)

  

0被摧毁

我认为此时您的矢量已重新分配,需要将对象复制到新存储中。原始向量中索引为0的对象在进程中被销毁

  

1被摧毁

本地Box被销毁

  

2创建

现在是本地Box的时间,索引为2

  

0被摧毁   1被摧毁

再次向量被重新分配并需要销毁原始位置中的对象

  

2被摧毁

本地变量被破坏

答案 3 :(得分:0)

这种情况正在发生,因为vector正在使用Box的默认复制运算符来创建对象的第二个实例,绕过您定义的构造函数。实际上,您确实有两个BoxbboxList[i]的实例。

注意如果将赋值运算符明确定义为private:

,编译器会发生什么
class Box
{
    public:
        int x;
        Box(int x);
        ~Box();

    private:
        Box& operator=( const Box& second ) {};
};

std中的编译器错误泛滥是由于在内部使用operator =来创建b对象的精确副本。

答案 4 :(得分:0)

您将获得一个自动生成的复制构造函数。 如果你替换

Box b(i);
boxList.push_back(b);

boxList.emplace_back(i);

你得到了建设。 如果你也使用reserve(3)(在添加项目之前),向量不必移动(调用析构函数的另一个可能原因),那么析构函数应该被正确调用3次