析构函数调用

时间:2015-07-17 10:32:20

标签: c++ stl

我试图理解类的向量。第一课:

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]f内返回不同的值。

6 个答案:

答案 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在做什么:

  1. 在旧标准(低于C ++ 11)中,它只是通过副本传递对象,因此vector会创建传递给push_back的对象的副本。
  2. 在较新的标准(&gt; = C ++ 11)中,它只使用了移动构造函数,它避免了复制,但仍然使用move-constructor创建了一个对象。
  3. 为避免这种情况,您可以使用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
    

    definition

    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