析构函数调用
Destructor calls
我正在尝试理解 classes 的向量。首先是 class:
class A {
public:
A() { cout << "A ctor" << endl; }
~A() { cout << "A dtor" << endl; }
};
然后是代码:
int main() {
vector<A> v;
cout << "after vector created" << endl;
v.push_back(A());
cout << "after one A added" << endl;
}
如果我运行这个程序,输出是:
after vector created
A ctor
A dtor <<-- why this??
after one A added
A dtor
我不明白,什么是额外的析构函数调用。构造函数和析构函数调用不应该配对吗?
其次,如果我将向量按值传递给函数,为什么不调用构造函数?
void f(vector<A> v);
我检查过,f
有 A
的副本,因为如果我修改它的内部状态,它不会在原始向量中被修改,并且 &v[0]
returns 不同f
.
内的值
因为 A
对象被复制到 vector 中,因此您在对 push_back()
的调用中传递的临时对象在复制后立即被销毁。
事情是这样的:
vector<A> v;
cout << "after vector created" << endl;
v.push_back(A());
// <<-- A temporary A object is created by calling A(). Then, the copy constructor of A is called to insert it into the vector.
// The temporary A object is destroyed.
cout << "after one A added" << endl;
// The A object in the vector is destroyed
你没有得到完整的图片,因为你没有跟踪复制构造函数:
class A {
public:
A() { cout << "A ctor" << endl; }
~A() { cout << "A dtor" << endl; }
//trace copy constructor
A(const A&) { cout << "A copy ctor" << endl; }
};
现在输出是:
after vector created
A ctor
A copy ctor
A dtor
after one A added
A dtor
您创建了一个 A
类型的临时对象以传递给 push_back
。然后使用复制构造函数复制它,销毁临时文件,然后在 main
函数末尾将副本与 std::vector
一起销毁。
是的,他们是一对。对于每个 dtor 调用,都有一个 ctor 调用。这可能是默认构造函数(打印 "A ctor")或复制构造函数(不打印任何内容 - 它是编译器生成的)。
Shouldn't the constructor and destructor calls be paired?
是的,他们是"paired";对于创建的每个对象,都会调用其析构函数。本质上,您所做的是因为您没有在此处追踪隐式生成的复制构造函数。它由临时 A()
生成,并且由于 class A
没有与之关联的移动,因此将此临时的副本插入向量中。如果 class A
有移动构造函数,它将用于 "move" 将对象放入向量中,因此您会看到使用移动构造函数而不是复制构造函数。
一旦 C++11 及更高版本包含在组合中,这里可能会涉及一些成员;包括复制构造函数、移动构造函数和赋值运算符。
#include <iostream>
#include <vector>
using namespace std;
class A {
public:
A() { cout << "A ctor" << endl; }
~A() { cout << "A dtor" << endl; }
A(const A&) { cout << "A copy ctor" << endl; }
A(A&&) { cout << "A move ctor" << endl; }
A& operator=(const A&) { cout << "An assignment" << endl; return *this; }
A& operator=(A&&) { cout << "A move assignment" << endl; return *this; }
};
int main() {
vector<A> v;
cout << "after vector created" << endl;
v.push_back(A());
cout << "after one A added" << endl;
}
随输出;
after vector created
A ctor
A move ctor
A dtor
after one A added
A dtor
关于第二个查询;
...if I pass the vector by value to a function, why are the constructors are not called?
它们是,复制构造函数将用于复制向量的内容。如果使用 C++11,您还可以在调用函数时移动整个向量(如果调用函数中不再需要向量)。
在v.push_back(A());
行中,你在方法中输入的类型A的参数是构造函数调用A()的结果,我想你很容易理解为什么调用构造函数。
现在请记住,传递的参数将一直存在到 push_back 方法 return。所以在 push_back 方法结束时,你的对象将被销毁。这是一个简单的例子
if( ... )
{
int a = 42;
}
a = 12; //error: use of undeclared identifier 'a'
a 存在于 if 语句的范围内,所以当你存在时,语句 a 被销毁。你的论点也一样。
其次,为什么这里没有调用你的构造函数void f(vector<A> v);
?
这是因为调用了复制构造函数。您将已构造的对象按值传递给方法,因此会生成一个副本。但是你没有自己定义拷贝构造函数,所以编译器为你创建了一个并调用它。
如果您想了解它是如何工作的,请查看我制作的 Live example。
如果您不知道复制构造函数是什么,请检查 this
如果你看到向量的push_back
的definition:
// [23.2.4.3] modifiers
/**
* @brief Add data to the end of the %vector.
* @param __x Data to be added.
*
* This is a typical stack operation. The function creates an
* element at the end of the %vector and assigns the given data
* to it. Due to the nature of a %vector this operation can be
* done in constant time if the %vector has preallocated space
* available.
*/
void
push_back(const value_type& __x)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
__x);
++this->_M_impl._M_finish;
}
else
#if __cplusplus >= 201103L
_M_emplace_back_aux(__x);
#else
_M_insert_aux(end(), __x);
#endif
}
#if __cplusplus >= 201103L
void
push_back(value_type&& __x)
{
emplace_back(std::move(__x));
}
#endif
这里很容易看出push_back
在做什么:
- 在较旧的标准(低于 C++11)中,它只是通过副本传递对象,因此 vector 创建您传递给
push_back
的对象的副本。
- 在较新的标准 (>= C++11) 中,它仅使用移动构造函数来避免复制,但仍然使用移动构造函数创建对象。
为避免这种情况,您可以使用 emplace_back
:
class A {
public:
A(int i = 0) { cout << "A ctor" << endl; }
~A() { cout << "A dtor" << endl; }
};
int main() {
vector<A> v;
cout << "after vector created" << endl;
v.emplace_back();
cout << "after one A added" << endl;
}
这将通过将方法的参数传递给 class 对象的构造函数,在 vector 的内存 space 中创建一个 class A
的对象,所以它不会被复制或移动:
after vector created
A ctor
after one A added
A dtor
P.S。关于你得到的:
after vector created
A ctor
A dtor
after one A added
A dtor
你在这里看不到 ctor-dtor 配对,因为在 std::vector::push_back
中,正如我上面提到的,没有通过默认构造函数构造 class A 的对象(你在其中打印 A ctor
) 但是一个复制构造函数或一个移动构造函数(取决于你编译的标准和实现细节)。
因此,它实际上是成对的,但您只是看不到它,因为您没有实现(覆盖隐式声明的)移动和复制构造函数。如果你这样做,你会看到这样的东西:
A default constructor
A copy/move constructor
A destructor
A destructor
我正在尝试理解 classes 的向量。首先是 class:
class A {
public:
A() { cout << "A ctor" << endl; }
~A() { cout << "A dtor" << endl; }
};
然后是代码:
int main() {
vector<A> v;
cout << "after vector created" << endl;
v.push_back(A());
cout << "after one A added" << endl;
}
如果我运行这个程序,输出是:
after vector created
A ctor
A dtor <<-- why this??
after one A added
A dtor
我不明白,什么是额外的析构函数调用。构造函数和析构函数调用不应该配对吗?
其次,如果我将向量按值传递给函数,为什么不调用构造函数?
void f(vector<A> v);
我检查过,f
有 A
的副本,因为如果我修改它的内部状态,它不会在原始向量中被修改,并且 &v[0]
returns 不同f
.
因为 A
对象被复制到 vector 中,因此您在对 push_back()
的调用中传递的临时对象在复制后立即被销毁。
事情是这样的:
vector<A> v;
cout << "after vector created" << endl;
v.push_back(A());
// <<-- A temporary A object is created by calling A(). Then, the copy constructor of A is called to insert it into the vector.
// The temporary A object is destroyed.
cout << "after one A added" << endl;
// The A object in the vector is destroyed
你没有得到完整的图片,因为你没有跟踪复制构造函数:
class A {
public:
A() { cout << "A ctor" << endl; }
~A() { cout << "A dtor" << endl; }
//trace copy constructor
A(const A&) { cout << "A copy ctor" << endl; }
};
现在输出是:
after vector created
A ctor
A copy ctor
A dtor
after one A added
A dtor
您创建了一个 A
类型的临时对象以传递给 push_back
。然后使用复制构造函数复制它,销毁临时文件,然后在 main
函数末尾将副本与 std::vector
一起销毁。
是的,他们是一对。对于每个 dtor 调用,都有一个 ctor 调用。这可能是默认构造函数(打印 "A ctor")或复制构造函数(不打印任何内容 - 它是编译器生成的)。
Shouldn't the constructor and destructor calls be paired?
是的,他们是"paired";对于创建的每个对象,都会调用其析构函数。本质上,您所做的是因为您没有在此处追踪隐式生成的复制构造函数。它由临时 A()
生成,并且由于 class A
没有与之关联的移动,因此将此临时的副本插入向量中。如果 class A
有移动构造函数,它将用于 "move" 将对象放入向量中,因此您会看到使用移动构造函数而不是复制构造函数。
一旦 C++11 及更高版本包含在组合中,这里可能会涉及一些成员;包括复制构造函数、移动构造函数和赋值运算符。
#include <iostream>
#include <vector>
using namespace std;
class A {
public:
A() { cout << "A ctor" << endl; }
~A() { cout << "A dtor" << endl; }
A(const A&) { cout << "A copy ctor" << endl; }
A(A&&) { cout << "A move ctor" << endl; }
A& operator=(const A&) { cout << "An assignment" << endl; return *this; }
A& operator=(A&&) { cout << "A move assignment" << endl; return *this; }
};
int main() {
vector<A> v;
cout << "after vector created" << endl;
v.push_back(A());
cout << "after one A added" << endl;
}
随输出;
after vector created
A ctor
A move ctor
A dtor
after one A added
A dtor
关于第二个查询;
...if I pass the vector by value to a function, why are the constructors are not called?
它们是,复制构造函数将用于复制向量的内容。如果使用 C++11,您还可以在调用函数时移动整个向量(如果调用函数中不再需要向量)。
在v.push_back(A());
行中,你在方法中输入的类型A的参数是构造函数调用A()的结果,我想你很容易理解为什么调用构造函数。
现在请记住,传递的参数将一直存在到 push_back 方法 return。所以在 push_back 方法结束时,你的对象将被销毁。这是一个简单的例子
if( ... )
{
int a = 42;
}
a = 12; //error: use of undeclared identifier 'a'
a 存在于 if 语句的范围内,所以当你存在时,语句 a 被销毁。你的论点也一样。
其次,为什么这里没有调用你的构造函数void f(vector<A> v);
?
这是因为调用了复制构造函数。您将已构造的对象按值传递给方法,因此会生成一个副本。但是你没有自己定义拷贝构造函数,所以编译器为你创建了一个并调用它。
如果您想了解它是如何工作的,请查看我制作的 Live example。
如果您不知道复制构造函数是什么,请检查 this
如果你看到向量的push_back
的definition:
// [23.2.4.3] modifiers
/**
* @brief Add data to the end of the %vector.
* @param __x Data to be added.
*
* This is a typical stack operation. The function creates an
* element at the end of the %vector and assigns the given data
* to it. Due to the nature of a %vector this operation can be
* done in constant time if the %vector has preallocated space
* available.
*/
void
push_back(const value_type& __x)
{
if (this->_M_impl._M_finish != this->_M_impl._M_end_of_storage)
{
_Alloc_traits::construct(this->_M_impl, this->_M_impl._M_finish,
__x);
++this->_M_impl._M_finish;
}
else
#if __cplusplus >= 201103L
_M_emplace_back_aux(__x);
#else
_M_insert_aux(end(), __x);
#endif
}
#if __cplusplus >= 201103L
void
push_back(value_type&& __x)
{
emplace_back(std::move(__x));
}
#endif
这里很容易看出push_back
在做什么:
- 在较旧的标准(低于 C++11)中,它只是通过副本传递对象,因此 vector 创建您传递给
push_back
的对象的副本。 - 在较新的标准 (>= C++11) 中,它仅使用移动构造函数来避免复制,但仍然使用移动构造函数创建对象。
为避免这种情况,您可以使用 emplace_back
:
class A {
public:
A(int i = 0) { cout << "A ctor" << endl; }
~A() { cout << "A dtor" << endl; }
};
int main() {
vector<A> v;
cout << "after vector created" << endl;
v.emplace_back();
cout << "after one A added" << endl;
}
这将通过将方法的参数传递给 class 对象的构造函数,在 vector 的内存 space 中创建一个 class A
的对象,所以它不会被复制或移动:
after vector created
A ctor
after one A added
A dtor
P.S。关于你得到的:
after vector created
A ctor
A dtor
after one A added
A dtor
你在这里看不到 ctor-dtor 配对,因为在 std::vector::push_back
中,正如我上面提到的,没有通过默认构造函数构造 class A 的对象(你在其中打印 A ctor
) 但是一个复制构造函数或一个移动构造函数(取决于你编译的标准和实现细节)。
因此,它实际上是成对的,但您只是看不到它,因为您没有实现(覆盖隐式声明的)移动和复制构造函数。如果你这样做,你会看到这样的东西:
A default constructor
A copy/move constructor
A destructor
A destructor