【问题标题】:C++ destructor called twice with stack allocated compound objects使用堆栈分配的复合对象调用 C++ 析构函数两次
【发布时间】:2019-02-24 19:20:29
【问题描述】:

我有一个复合类(包含其他实例、指针和引用的实例)。 当容器实例被销毁时,将调用包含实例的析构函数(我可以接受,这是逻辑)。但问题是,如果包含的实例是堆栈分配的,则在超出范围时会再次调用析构函数。

这是编码错误还是编译器问题?

最干净的修复方法是什么?

这是我的示例:

#include <iostream>

using std::cout;
using std::endl;

class A {
public:
  int i;
  A(int i_) : i(i_) {
    cout << "A(): " << i << endl;
  }
  ~A() {
    cout << "~A(): " << i << endl;
  }
};

class B {
public:
  A a;
  int b;
  B(const A& a_) : a(a_) {
    cout << "B(): " << a.i << endl;
  }
  ~B() {
    cout << "~B(): " << a.i << endl;
  }
};

int main(void) {
  for(int c = 0; c < 3; ++c) {
    A a(c+1);
    B b(a);
    cout << b.a.i << endl;
  }
  return 0;
}

输出是:

A(): 1
B(): 1
1
~B(): 1
~A(): 1
~A(): 1
A(): 2
B(): 2
2
~B(): 2
~A(): 2
~A(): 2
A(): 3
B(): 3
3
~B(): 3
~A(): 3
~A(): 3

编译器是 gcc 7.3.0

【问题讨论】:

  • B 包含一个 A .... 它只是没有调用你认为它应该调用的构造函数
  • 您已经知道~B 调用~A。但是除了B 的实例之外,您还有另一个A 的免费实例。所以A 的两个实例:一个免费,一个在B 内部。两者的析构函数都被调用。
  • 为什么觉得这里有问题?
  • 在跟踪构造函数/析构函数调用时,不要忘记复制/移动构造函数。

标签: c++ destructor


【解决方案1】:

每个对象只调用一次析构函数。您在输出中看不到的是,当您构造b 时,您使用复制构造函数(在您的情况下是编译器生成的)创建acopy。这不会产生任何输出,但当然也会调用副本的析构函数。

如果我们将输出添加到复制构造函数,我们可以看到实际发生的情况:

A(const A& a_) : i(a_.i) {
  cout << "A(const A&): " << i << endl;
}

输出显示每个A 被复制一次,导致“重复”(不是真的)析构函数调用(live demo)。如果您想避免复制对象,请查看 C++11 的 std::move,在 elsewhere on this site 中进行了深入解释。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-24
    • 2020-12-25
    • 2012-09-12
    • 1970-01-01
    • 2016-03-02
    • 1970-01-01
    • 2016-09-06
    相关资源
    最近更新 更多