我试图理解类的向量。第一课:
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]
在f
内返回不同的值。
答案 0 :(得分:4)
您没有全面了解,因为您没有跟踪复制构造函数:
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
。然后使用复制构造函数复制它,临时销毁,然后复制将与std::vector
函数末尾的main
一起销毁。
答案 1 :(得分:3)
不应该将构造函数和析构函数调用配对吗?
是的,他们是&#34;配对&#34 ;;对于创建的每个对象,将调用其析构函数。基本上你正在做的是因为你没有在这里描述隐式生成的复制构造函数。它由临时A()
生成,并且由于类A
没有与之关联的移动,因此将此临时的副本插入到向量中。如果类A
有一个移动构造函数,它将用于&#34;移动&#34;将对象放入向量中,因此您将看到正在使用的移动构造函数而不是复制构造函数。
一旦混合中包含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
关于第二个查询;
...如果我将值向量传递给函数,为什么不调用构造函数?
它们是,复制构造函数将用于复制向量的内容。如果使用C ++ 11,您还可以在调用函数时移动整个向量(如果调用函数中不再需要向量)。
答案 2 :(得分:2)
如果您看到向量push_back
的{{3}}:
// [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
在做什么:
push_back
的对象的副本。为避免这种情况,您可以使用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;
}
这只是通过将方法的参数传递给类对象的构造函数,在向量的内存空间中创建类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
中没有通过默认构造函数(在其中打印A ctor
)构建一个A类对象,但是副本-constructor或move-constructor(取决于编译和实现细节的标准)。
所以,它实际上是配对的,但你只是没有看到它因为你没有实现(覆盖隐式声明的)移动和复制构造函数。如果你这样做,你会看到类似的东西:
A default constructor
A copy/move constructor
A destructor
A destructor
答案 3 :(得分:1)
是的,他们是配对的。对于每个dtor呼叫,都有一个ctor呼叫。这可能是默认的ctor(打印“A ctor”)或复制ctor(不打印任何内容 - 它是编译器生成的)。
答案 4 :(得分:1)
在行v.push_back(A());
中,您在方法中放入的类型A的参数是构造函数调用A()的结果,我认为您可以很容易地理解为什么调用构造函数。
现在请记住,传递的参数将一直存在,直到push_back方法返回。所以在push_back方法结束时,你的对象将被销毁。这是一个简单的例子
if( ... )
{
int a = 42;
}
a = 12; //error: use of undeclared identifier 'a'
在if语句的范围内生活,所以当你生活语句时,a被销毁。你的论点也是一样。
其次为什么你的构造函数不在这里调用void f(vector<A> v);
?
这是因为调用了复制构造函数。您已经构造的对象按值传递给方法,因此创建了一个副本。但是你没有自己定义一个拷贝构造函数,所以编译器为你创建了一个并调用它。
如果你想了解它是如何工作的,请查看我所做的Live example。
如果您不知道复制构造函数是什么,请检查this
答案 5 :(得分:0)
因为A
对象被复制到向量中,因此在复制后立即销毁在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