为什么类的析构函数会被调用两次?

34

如果这个问题听起来很傻,我向您道歉。我在Stack Overflow关注专家并尝试一些例子,这是其中之一。我确实尝试了搜索选项,但没有找到这种类型的答案。

class A
{
    public:
         A(){cout<<"A Contruction"<<endl;}
        ~A(){cout<<"A destruction"<<endl;}
};

int main()
{
    vector<A> t;
    t.push_back(A()); // After this line, when the scope of the object is lost.
}

为什么类的析构函数会被调用两次?

不要认为SO支持<blink>标签。不过,也许可以在元中提出一个功能请求?我想Jeff会喜欢它的! :-) - Franci Penov
@pst:谢谢,我正在编辑,还没来得及保存它就完成了。 - dicaprio
6
只是想知道,为什么这个社区是维基页面? - jasonline
@dicaprio:跟进[http://stackoverflow.com/questions/2657752/extraneous-calls-to-copy-constructor-and-destructor] - Lazer
使用t.emplace_back()(C++0x)代替,只有一个A对象和一个析构函数调用。 - rafak
以防有人在发现他们的析构函数被调用两次导致尝试释放资源两次后到达这里 - 要解决您的问题,您可能需要为具有在析构函数中释放的资源的类提供_move constructor_,因此您可以基本上“使捐赠者对象无效”进行复制。 这解决了我的问题:https://learn.microsoft.com/en-us/cpp/cpp/move-constructors-and-move-assignment-operators-cpp?view=msvc-170 - saxbophone
5个回答

51

为了添加元素,会在临时对象上调用复制构造函数。在执行push_back()后,临时对象被销毁 - 这是第一次调用析构函数。然后vector实例超出作用域并销毁所有存储的元素 - 这是第二次析构函数调用。


@sharptooth&@Andreas:谢谢。看到你们的回复后,我尝试了一下,定义了一个拷贝构造函数A(ConstA&),是的,它被调用了。 - dicaprio
4
@dicaprio:当然,直到你定义了自己的复制构造函数之前,编译器都会使用其自动生成的复制构造函数。 - sharptooth

28

这将向您展示正在发生的事情:链接

struct A {
  A() { cout << "contruction\n"; }
  A(A const& other) { cout << "copy construction\n"; }
  ~A() { cout << "destruction\n"; }
};

int main() {
  vector<A> t;
  t.push_back(A());
}

!Roger: 在你发帖之前,我也尝试了同样的事情,我明白了,谢谢!! - dicaprio
如何允许拷贝构造函数正确拷贝并使用 cout,而不调用自身和初始化构造函数?唯一的方法是一个一个地分配字段吗? - ar2015

4

当传递给push_back的临时变量被销毁时,析构函数会被调用一次;当t中的元素被销毁时,析构函数也会被调用一次。


3
由于有两个对象:作为push_back参数的对象和在vector t中新添加的元素,因此会调用两次析构函数。 STL容器存储副本。在您的示例中,通过push_back添加到vector中的元素是从传递给push_back的参数复制构造的。该参数是A(),这是一个临时对象,请参见此处(第4个变体)
稍微扩展一下回答(尽管您没有明确要求):了解临时对象何时被销毁可能很有用。标准(N4140)在12.2 p3中非常清楚地说明了这一点。

...临时对象在创建它们的点所在的(词法)完整表达式(1.9)评估的最后一步中被销毁...

附注: 如果使用emplace_back,则只有一个对象。容器中的新元素直接从emplace_back的参数构造而成。许多STL容器在C++11中学习了emplace变体。


0

很可能是你的对象被复制了。因此,复制对象和原始对象的析构函数都会使调用计数=2。

例如:即使你将对象引用传递给某个类,这在内部也会调用复制构造函数。为了避免这种情况,子类(你将父类引用传递给它)必须是:

Parent *const &p parentRef;  //Child.h

然后,父对象将被传递为;

// Parent.cpp
Parent *parentObj = this;
Child *childObj = Child(parentObj);

此外,您可以通过覆盖来调试复制构造函数的调用;
Parent(const Parent& object){
        cout <<"copy called.." << endl;
}
...

更多信息 @stackoverflow@


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接