我正在学习理解类构造函数和析构函数。 我编写了一个小的控制台代码,用于将一个类实例添加到向量中。一切都很好,花花公子,但我无法理解的是,在向量中添加一个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;
}
答案 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
末尾添加了一个打印件):
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
被破坏,所以它就是内容