对象的奇怪破坏

Strange Destruction of objects

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

#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

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

0 Destroyed
1 Destroyed
2 Destroyed

谁能解释一下。

这有两个原因。

首先,push_back 有时会扩展向量的大小,重新分配 space,将所有对象复制到那里,并销毁旧对象。要摆脱这种重新分配的影响,您可以在循环之前添加 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 被销毁。这就解释了为什么上面输出中的每个对象在创建后很快就被销毁了。

最后三个调用当然是在程序退出时。

0 Created

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

0 Destroyed

并销毁

1 Created

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

0 Destroyed

我认为此刻您的向量已重新分配并需要将对象复制到其新存储。原vector中index为0的对象在处理过程中被销毁

1 Destroyed

本地Box被销毁

2 Created

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

0 Destroyed 1 Destroyed

vector 再次被重新分配,需要销毁原始位置的对象

2 Destroyed

局部变量销毁

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

请注意,如果将赋值运算符显式定义为私有,编译器会发生什么情况:

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

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

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

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

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

boxList.emplace_back(i);

您可以进行现场施工。 如果您还使用 reserve(3)(在添加项目之前),向量将不必四处移动(调用析构函数的另一个可能原因),那么应该恰好调用析构函数 3 次

其他答案正确地提到了隐式 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 的实现可以决定何时重新分配其内部数组。