代码之家  ›  专栏  ›  技术社区  ›  WilliamKF

在什么情况下C++调用析构函数将不被调用?

  •  41
  • WilliamKF  · 技术社区  · 14 年前

    我知道我的析构函数是在堆栈的正常展开和抛出异常时调用的,而不是在调用exit()时调用的。

    有没有其他情况下我的析构函数不会被调用?像sigint或sigsegv这样的信号呢?我假定对于sigsegv,它们不是被调用的,但是对于signint,它们是,我如何知道哪些信号将展开堆栈?

    有没有其他情况下他们不会被召唤?

    8 回复  |  直到 7 年前
        1
  •  46
  •   Community Egal    7 年前

    是否有其他不调用它们[析构函数]的情况?

    1. 跳远:这干扰了自然堆栈展开过程,并且经常导致C++中未定义的行为。
    2. 过早退出(您已经指出了这些,尽管值得注意的是,由于抛出异常而导致的堆栈展开时抛出会导致未定义的行为,这就是为什么我们不应该抛出DTR的原因)
    3. 从构造函数引发不会为类调用DTOR。这就是为什么,如果在一个ctor中分配由多个不同指针(而不是智能指针)管理的多个内存块,则需要使用函数级的try-blocks,或者避免使用初始值设定项列表,并在ctor主体中有一个try/catch块(或者更好的方法是,只使用一个智能指针(如scoped_ptr),因为任何成员都已成功初始化,所以初始值设定项列表中的far将被销毁,即使不会调用类dtor)。
    4. 正如所指出的,当通过基指针删除类时,未能使dtor成为虚拟的,可能无法调用子类dtor(未定义的行为)。
    5. 未能为运算符new/new[]调用调用匹配的运算符delete/delete[](未定义的行为-可能无法调用dtor)。
    6. 在deallocate节中使用placement new和自定义内存分配器时,未能手动调用DTOR。
    7. 使用像memcpy这样的函数,它只将一个内存块复制到另一个内存块,而不调用copy ctors。MEM*函数在C++中是致命的,因为它们在类的私有数据上翻转、重写VTABLE等。结果通常是未定义的行为。
    8. 在不完整类型上实例化某些智能指针(auto_ptr),请参见 discussion
        2
  •  7
  •   Craig M. Brandenburg Martin    7 年前

    C++标准没有说明如何处理特定的信号——许多实现可能不支持。 SIGINT 等。如果 exit() abort() terminate() 被称为。

    编辑: 我刚刚通过了C++标准的快速搜索,我找不到任何指明信号如何与对象生命周期交互的东西-也许有比我更好的FU标准的人能找到一些东西?

    进一步编辑: 在回答另一个问题时,我在标准中发现:

    退出作用域时(但是 完成),析构函数(12.4)为 调用所有构造的对象 具有自动存储时间 (3.7.2)(命名对象或临时对象) 在该范围内声明的,在 它们的相反顺序 宣言。

    因此,似乎必须在收到信号时调用析构函数。

        3
  •  3
  •   DanDan    14 年前

    另一个不会被调用的情况是,如果您使用的是多态性,并且没有使您的基析构函数成为虚拟的。

        4
  •  3
  •   Alex B    14 年前

    信号 独自一人 不会影响执行 当前线程 因此调用析构函数,因为它是一个不同的执行上下文,有自己的堆栈,而您的对象不存在。这就像一个中断:它被处理在执行上下文之外的某个地方,如果被处理,控制权就会返回到程序。

    与多线程一样,C++ 语言 不知道信号的概念。这两个标准完全正交,由两个不相关的标准规定。它们如何交互取决于实现,只要它不违反任何一个标准。

    另一种情况是,当对象的析构函数不被调用时,它的构造函数抛出异常。不过,成员的析构函数仍将被调用。

        5
  •  2
  •   Kirill V. Lyadvinsky    14 年前

    abort 如标准所述,终止程序而不为自动或静态存储持续时间的对象执行析构函数。对于其他情况,您应该阅读特定于实现的文档。

        6
  •  2
  •   karunski    14 年前

    如果一个函数或方法有一个throw规范,并且抛出了规范未涵盖的内容,那么默认行为是立即退出。堆栈没有解开,也不调用析构函数。

    POSIX信号是一个操作系统特定的构造,没有C++对象范围的概念。一般来说,除了信号可能会做任何事情,除了捕获信号,设置全局标志变量,然后在信号处理程序退出之后,在C++代码中处理它。

    GCC的最新版本允许您从同步信号处理程序中抛出异常,这会导致预期的展开和破坏过程。不过,这是非常特定于操作系统和编译器的

        7
  •  2
  •   Community Egal    7 年前

    这里有很多答案,但仍然不完整!

    我发现了另一个没有执行析构函数的案例。当异常跨越库边界时,总是会发生这种情况。

    请在此处查看更多详细信息:

    Destructors not executed (no stack unwinding) when exception is thrown

        8
  •  1
  •   nob    14 年前

    基本上有两种情况调用析构函数:在函数末尾的堆栈展开(或在异常处),如果某人(或引用计数器)调用了delete。

    在静态对象中会发现一种特殊情况——它们在程序结束时通过at_exit被破坏,但这仍然是第二种情况。

    哪个信号离开出口通过可能取决于,kill-9将立即终止进程,其他信号将告诉它退出,但具体取决于信号回调。