【问题标题】:Destructor Called Twice While No Copy Constructor or Assignment Operator Gets Callled析构函数被调用两次,而没有复制构造函数或赋值运算符被调用
【发布时间】:2011-09-05 17:29:06
【问题描述】:

我有一堂课:

class A
{
public:
    A()
    {
        std::cout << "Constructor called" << std::endl;
    }

    ~A()
    {
        std::cout << "Destructor called" << std::endl;
    }

    A(const A& another)
    {
        std::cout << "Copy constructor called" << std::endl;
    }

    A& operator=(const A& another)
    {
        std::cout << "Assignment operator= called" << std::endl;
    }
};

在我非常复杂的项目中,我启动应用后得到以下输出:

Constructor called

但是当我 Ctrl+C 终止我的应用程序时:

Destructor called
Destructor called

并且在整个过程中,没有调用拷贝构造函数和赋值运算符。

我的类A有动态内存分配,我必须在析构函数中释放它,但是析构函数不知何故被调用了两次,这很糟糕。

我不知道是什么导致了这个问题。

我用谷歌搜索了很多。很多关于“析构函数调用两次”的问题是因为隐式调用了复制构造函数(赋值运算符)。

谢谢。

彼得

【问题讨论】:

  • “赋值运算符”不应该是operator= 还是我遗漏了什么?
  • 抱歉,打错字了。我更正了。
  • 既然您显然可以访问源代码,您就不能简单地在析构函数中放置断点,看看发生了什么吗?
  • 您能否发布一个出现此问题的最小可编译示例?
  • 你能提供sn-ps来说明A在你的代码中是如何使用的吗?

标签: c++ destructor


【解决方案1】:

如果你以某种方式调用了析构函数两次,也许你有两个对象认为它们通过指针拥有它。

如果您确实有两个对象认为他们拥有这个,请考虑使用引用计数指针,例如 Boost::shared_ptr 或 tr1::shared_ptr 来包含它。这样你就不必担心最终谁调用了析构函数。

除了调试器之外,您还可以尝试 Valgrind (memcheck) 来查找您的程序删除已释放对象的位置。在这种情况下,它可能不会提供比调试器更多的细节,但你迟早应​​该学会使用 Valgrind (memcheck)。

另一个偏执的策略是确保在删除内部指针后将它们设置为 NULL。

【讨论】:

  • 我尝试为我的 A 类使用 boost::shared_ptr,但问题消失了。现在我可以确定来自第 3 方的基础代码肯定存在一些问题,因为我使用的是他们的内存管理类 RefCountingPointer/RefCountingObject。整个项目的原始代码是使用这两个类。哦,天哪……
【解决方案2】:

很可能您有另一个未显示的构造函数,或者您多次调用析构函数,无论是显式还是通过delete

调试器或额外的couts 将比我们在这种情况下更有帮助。

【讨论】:

    【解决方案3】:

    在析构函数中设置断点。然后,无论何时调用它,您都可以获取堆栈跟踪并查看它是从哪里调用的。

    编辑:

    可以期望复制省略会对像这些琐碎的调试语句造成严重破坏。

    【讨论】:

    • 您能否详细说明复制省略语句?似乎如果省略了副本,则相应的析构函数也必须如此。
    • 我试图将我的 Eclipse CDT 调试器附加到正在运行的进程,但是当我按 Ctrl+C 到正在运行的进程时,调试器在非常深的源代码(系统或 boost 库)中的某处停止(似乎堆栈跟踪中的线程睡眠)。我什至无法在我的析构函数中插入断点:断点属性问题:安装 - 失败。这个过程就挂在那里。在我终止调试器,然后 Ctrl+C 终止正在运行的进程后,析构函数被调用了两次,然后是分段错误,然后退出。
    • 如果复制构造函数被移除,那么销毁也会被移除。创造/毁灭将始终保持平衡。
    • @Peter Lee:然后提出一个关于为什么你的调试器不工作的问题。
    • 我终于重新创建了项目,并重新添加到工作区,然后(无法安装断点)问题消失了。有时,Eclipse CDT 非常不稳定。
    【解决方案4】:

    在我的例子中,我没有使用指针,但析构函数仍然被调用了两次。我为解决这个问题所做的是覆盖复制赋值运算符MyClass&amp; operator=(const MyClass&amp; other)。不太清楚为什么自动生成的运算符会导致问题,但似乎确实如此。

    【讨论】:

      猜你喜欢
      • 2012-01-01
      • 2016-07-12
      • 2013-12-11
      • 1970-01-01
      • 2013-07-03
      • 1970-01-01
      • 2022-07-05
      • 2015-04-28
      相关资源
      最近更新 更多