【问题标题】:Destructor calls析构函数调用
【发布时间】:2015-10-07 01:21:55
【问题描述】:

我正在尝试理解类的向量。首先是班级:

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 的副本,因为如果我修改它的内部状态,它不会在原始向量中被修改,&amp;v[0]f 中返回不同的值。

【问题讨论】:

  • 那是因为 vector::push_back 总是复制你刚刚传入的参数,但是通过复制构造函数。 werid析构函数由temporaray A对象调用,也就是你传入的那个。另外,如果你想直接在vector中创建一个元素,你可以使用emplace_back,C++11支持。
  • 正如你上面所说的,是你传入的对象。当对象超出范围时调用析构函数。您传入的 A() 对于 push_back 函数是本地的。一旦超出范围,它的析构函数就会被调用。
  • 我有一个可能感兴趣的相关问题:stackoverflow.com/questions/28716209/…
  • 感谢所有答案,我选择了提到 C++11 的那一个。

标签: c++ stl


【解决方案1】:

因为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

【讨论】:

    【解决方案2】:

    你没有得到完整的图片,因为你没有跟踪复制构造函数:

    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 一起被销毁。

    【讨论】:

    • 虽然我害怕编辑 15k 评级的人的答案,但我仍然通过添加 emplace_back 来做到这一点。那么,有什么理由删除它?只是好奇。
    • @VictorPolevoy 这是一个很好的建议,但您应该在自己的答案中添加它:我只是直接回答问题。
    • 好吧,我认为最好详细说明好的答案,让它们变得更好。
    • @VictorPolevoy:一般来说是这样,但有一个限制。您添加了太多并有效地改变了这个答案的意思。大多数情况下,“详细说明”其他人的答案是不受欢迎的。
    • @TartanLlama Question on meta.
    【解决方案3】:

    是的,它们是配对的。对于每个 dtor 调用,都有一个 ctor 调用。这可能是默认 ctor(打印“A ctor”)或复制 ctor(不打印任何内容 - 它是编译器生成的)。

    【讨论】:

      【解决方案4】:

      构造函数和析构函数调用不应该成对吗?

      是的,它们是“配对的”;对于创建的每个对象,都会调用其析构函数。本质上,您所做的是因为您没有在此处追踪隐式生成的复制构造函数。它由临时 A() 生成,并且由于类 A 没有与之关联的移动,因此将这个临时的副本插入到向量中。如果A 类有一个移动构造函数,它将用于将对象“移动”到向量中,因此您会看到使用移动构造函数而不是复制构造函数。

      一旦 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,您还可以在调用函数时移动整个向量(如果调用函数中不再需要向量)。

      【讨论】:

        【解决方案5】:

        v.push_back(A());这行你在方法中输入的类型A的参数是构造函数调用A()的结果,我想你可以很容易理解为什么调用构造函数。 现在请记住,传递的参数将一直存在,直到 push_back 方法返回。因此,在 push_back 方法结束时,您的对象将被销毁。这是一个简单的例子

        if( ... )
        {
             int a = 42;
        }
        a = 12; //error: use of undeclared identifier 'a'
        

        a 存在于 if 语句的范围内,因此当您存在时,a 将被销毁。你的论点也是一样。

        其次,为什么这里没有调用你的构造函数 void f(vector&lt;A&gt; v); ? 这是因为调用了复制构造函数。您将已经构建的对象按值传递给方法,因此会创建一个副本。但是您并没有自己定义复制构造函数,所以编译器为您创建了一个并调用它。

        如果您想了解它是如何工作的,请查看我制作的 Live example

        如果你不知道什么是复制构造函数,请查看this

        【讨论】:

        • "这里为什么要调用拷贝构造函数?是因为调用了拷贝构造函数。"大声笑
        【解决方案6】:

        如果您看到向量的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)中,它只是通过副本传递对象,因此向量会创建您传递给 push_back 的对象的副本。
        2. 在较新的标准 (>= C++11) 中,它仅使用 move-constructor 来避免复制,但仍使用 move-constructor 创建对象。

        为避免这种情况,您可以使用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;
        }
        

        这将通过将方法的参数传递给类对象的构造函数,在vector的内存空间内创建一个类A的对象,因此它不会被复制或移动:

        after vector created
        A ctor
        after one A added
        A dtor
        

        Test emplace_back

        附:关于你得到了什么:

        after vector created
        A ctor
        A dtor
        after one A added
        A dtor
        

        您在这里看不到 ctor-dtor 配对,因为我在上面提到的 std::vector::push_back 不是通过默认构造函数(在其中打印 A ctor)构造 A 类的对象,而是通过复制构造函数或move-constructor(取决于您编译的标准和实现细节)。

        因此,它实际上是成对的,但您只是看不到它,因为您没有实现(覆盖隐式声明的)移动和复制构造函数。如果你这样做,你会看到这样的东西:

        A default constructor
        A copy/move constructor
        A destructor
        A destructor
        

        【讨论】:

          猜你喜欢
          • 2011-04-16
          • 1970-01-01
          • 2014-05-03
          • 2021-07-19
          • 1970-01-01
          • 2014-05-17
          • 1970-01-01
          相关资源
          最近更新 更多