Vector and push_back() behavior

Vector and push_back() behavior

本文关键字:behavior back and push Vector      更新时间:2023-10-16

我正在学习理解类的构造函数和析构函数。我写了一个小的控制台代码来添加一个类实例到一个向量。一切都很好,但我不明白的是,向vector中添加一个Object会触发析构函数两次。为什么会发生这种情况?

如果不添加任何对象,vector本身不会触发构造函数或析构函数,那么为什么要触发两次呢?

谁能解释一下为什么会这样?

#include <cstdio>
#include <vector>
class Test
{
    private:
        int value;
    public:
        Test()
        {
            printf("nClass constructor triggered.");
        };
        ~Test()
        {
            printf("nClass desctructor triggered.");
        }
};
int main()
{
    std::vector<Test> container;
    container.push_back( Test() );
    return 0;
}

更新:我向类中添加了一些更多的信息,以便获得更具体的输出,但是现在我注意到,随着向量的每一次添加,move构造函数和析构函数调用都会增加。这些调用的数量是否与向量内对象的数量有关,或者发生了什么?我漏了吗?如果问题太蠢,我很抱歉。下面是添加的代码:

#include <cstdio>
#include <vector>
class Test
{
    private:
        int value;
    public:
        // Constructor
        Test(int v=0)
        {
            value = v;
            printf("nn%i", value);
            printf("nClass constructor triggered.");
        };
        // Copy-move constructor
        Test(Test&&)
        {
            printf("nClass move-constructor triggered.");
        };
        // Destructor
        ~Test() 
        {
            value = 0;
            printf("nClass desctructor triggered.");
        }
};
int main()
{
    std::vector<Test> container;
    container.push_back( Test(1) );
    container.push_back( Test(2) );
    container.push_back( Test(3) );
    container.push_back( Test(4) );
    printf("nnPushback complete!");
    return 0;
}

您的向量包含您通过push_back()添加到它的对象的副本。第一个析构函数调用是由于在包含对push_back()调用的完整表达式的末尾销毁了您创建的临时函数。第二个析构函数是由于当vector本身被析构时,vector内部的副本也被析构而产生的。

您可以通过向main()添加诊断来说服自己:

int main()
{
    std::vector<Test> container;
    container.push_back( Test() );
    printf("nThis is before the vector is destroyed...");
    return 0;
}

您可以在这个实时示例中观察输出。

vector包含的副本是通过调用类自动生成的move构造函数(而不是使用默认构造)创建的,这就是为什么没有看到相应的构造诊断。

如果您定义了自己的移动构造函数(或复制构造函数,如下所示)来发出诊断,则输出将更接近您所期望的:

    Test(Test const&)
    {
        printf("nCopy construction triggered.");
    };

再一次,实例

因为您没有打印每个构造函数调用,所以您错过了move-constructor调用。除了您提供的默认构造函数之外,您的类还隐式地生成了移动和复制构造函数。

vector存储一个值,该值必须以某种方式初始化。通常,这是通过移动c-tor或复制c-tor来实现的,尽管也可以在vector内部直接创建对象,例如emplace_back

试着添加这个:

Test(Test&&)
{
    printf("nClass move constructor triggered.");
};

到您的类中,它应该将输出更改为更有意义的内容(我还在main末尾添加了打印):

Live On Coliru

Class constructor triggered.
Class moveconstructor triggered.
Class desctructor triggered.
Out of main scope.
Class desctructor triggered.

第一个析构函数调用销毁移出的类的"空"实例,而第二个调用在vector本身被销毁时触发。

push_back()不触发任何析构函数(在这种情况下)。

Test的析构函数的两个调用是:

1 -因为你向push_back()传递了一个临时对象,所以当push_back()完成时对象被销毁

2 -当程序结束时,所以vector被销毁,所以它的内容

为简单起见,让我们假设您正在使用c++ 03,并且移动语义还不可用。

添加复制构造函数以查看它是否也被触发

Test(const Test&)
{
    printf("nClass copy constructor triggered.");
};

输出
Class constructor triggered.
Class copy constructor triggered.
Class destructor triggered.
Class destructor triggered.

因此,有两个对象被构造/析构。

粗略地说,你的代码等于
int main()
{
    std::vector<Test> container;
    Test test;                    // first object created
    container.push_back(test);    // second object created by copying
    return 0;
}