析构函数调用

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);

我检查过,fA 的副本,因为如果我修改它的内部状态,它不会在原始向量中被修改,并且 &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_backdefinition

// [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在做什么:

  1. 在较旧的标准(低于 C++11)中,它只是通过副本传递对象,因此 vector 创建您传递给 push_back 的对象的副本。
  2. 在较新的标准 (>= 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

Test emplace_back

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