Vector和push_back()行为

时间:2015-10-09 10:09:37

标签: c++

我正在学习理解类构造函数和析构函数。 我编写了一个小的控制台代码,用于将一个类实例添加到向量中。一切都很好,花花公子,但我无法理解的是,在向量中添加一个Object会触发析构函数两次。为什么会这样?

如果我没有添加任何对象,该向量本身不会触发构造函数或析构函数,那么为什么它会发生两次呢?

有人可以解释为什么会这样吗?

#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-construction和析构函数调用都会增加。这些调用的数量是否与向量中的对象数量相关或发生了什么? 我有泄漏吗?对不起,如果太愚蠢的问题。 以下是添加的代码:

#include <cstdio>
#include <vector>

class Test
{
    private:
        int value;

    public:
        // Constructor
        Test(int v=0)
        {
            value = v;
            printf("\n\n%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("\n\nPushback complete!");
    return 0;
}

4 个答案:

答案 0 :(得分:5)

您的向量包含通过push_back()添加到其中的对象的副本。第一个析构函数调用是由在包含对push_back()的调用的完整表达式末尾创建的临时销毁引起的。第二个析构函数是由向量本身被破坏时向量内的副本引起的。

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

int main()
{
    std::vector<Test> container;

    container.push_back( Test() );

    printf("\nThis is before the vector is destroyed...");

    return 0;
}

您可以观察此live example中的输出。

您的向量包含的副本是通过为您的类调用自动生成的移动构造函数(而不是使用默认构造)来创建的,这就是您没有看到相应构造诊断的原因。

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

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

再次,live example

答案 1 :(得分:3)

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

向量存储一个值,该值必须以某种方式初始化。通常,这通过移动计算器或复制计算器发生,尽管也可以使用例如使用例如直接在矢量内创建对象。 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.

第一个析构函数调用会破坏移出&#34;清空&#34;你的类的实例,而第二个实例是在向量本身被销毁时触发。

答案 2 :(得分:2)

为了简单起见,我们假设您正在使用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;
}

答案 3 :(得分:2)

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

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

1 - 因为您将临时值传递给push_back(),因此在push_back()完成时该对象将被销毁

2 - 当程序结束时,vector被破坏,所以它就是内容