C++:std::runtime_error 对象会在 longjmp 中泄漏吗?

C++: will an std::runtime_error object leak in a longjmp?

假设我有一些 C++ 代码,其中有一个 try-catch 块,其中 catch 部分将触发长跳转:

#include <stdexcept>
#include <stdio.h>
#include <setjmp.h>

void my_fun()
{
    jmp_buf jump_buffer;
    if (setjmp(jump_buffer))
        return;
    
    try {
        std::string message;
        message.resize(100);
        snprintf(&message[0], 100, "error code %d\n", 3);
        throw std::runtime_error(message);
    }

    catch (std::runtime_error &e) {
        longjmp(jump_buffer, 1);
    }
}

由于 std::runtime_error 对象是在某处动态分配的,它会泄漏为其或字符串分配的内存吗?

这有点复杂。关于longjmp的有效性,the standard says:

A setjmp/longjmp call pair has undefined behavior if replacing the setjmp and longjmp by catch and throw would invoke any non-trivial destructors for any objects with automatic storage duration.

runtime_error有一个非平凡的析构函数,所以问题是异常对象是否具有“自动存储持续时间”。它不是。这表明 longjmp 应该没问题。

此外,exception object destruction can happen in one of two places:

The points of potential destruction for the exception object are:

  • when an active handler for the exception exits by any means other than rethrowing, immediately after the destruction of the object (if any) declared in the exception-declaration in the handler;

  • when an object of type std​::​exception_­ptr that refers to the exception object is destroyed, before the destructor of std​::​exception_­ptr returns.

longjmp 不是“重新抛出”。所以理论上,由于要点 1,这应该没问题。

也就是说,永远不要依赖这个。我非常怀疑 longjmp 的实现能否正确处理此问题,即使有些实现也可能不是您可以期望的。

我不明白为什么你会想做这样的事情。 但这个问题很有趣。我知道你不能跳进一个catch block, 并且跳出它(也允许转到 :( )。

似乎异常在离开 catch 范围后就被破坏了。没有任何泄漏。下面是测试程序的输出:

scope: normal_try_catch::try entered
exception constructed
scope : normal_try_catch::try left
scope : normal_try_catch::catch entered
scope : normal_try_catch::catch left
exception destructed
----------------------------
scope : long_jump_catch::try entered
exception constructed
scope : long_jump_catch::try left
scope : long_jump_catch::catch entered
scope : long_jump_catch::catch left
exception destructed
scope : long_jump_catch::leave because of setjmp entered
scope : long_jump_catch::leave because of setjmp left   

所以在跳转之前异常被清除了。

#include <stdexcept>
#include <iostream>
#include <string>
#include <setjmp.h>

class my_except :
    public std::exception
{
public:
    my_except() 
    {
        std::cout << "exception constructed" << std::endl;
    }

    ~my_except()
    {
        std::cout << "exception destructed" << std::endl;
    }
};

struct scope
{
    explicit scope(const std::string& scope) :
        m_scope{ scope }
    {
        std::cout << "scope : " << m_scope << " entered" << std::endl;
    }

    ~scope()
    {
        std::cout << "scope : " << m_scope << " left" << std::endl;
    }


private:
    std::string m_scope;
};


void normal_try_catch()
{
    try
    {
        scope s{ "normal_try_catch::try" };
        throw my_except();
    }
    catch(const std::exception&)
    {
        scope s{ "normal_try_catch::catch" };
    }
}

void long_jump_catch()
{
    jmp_buf jump_buffer;
    if (setjmp(jump_buffer))
    {
        scope s{ "long_jump_catch::leave because of setjmp" };
        return;
    }

    try
    {
        scope s{ "long_jump_catch::try" };
        throw my_except();
    }
    catch (const std::exception&)
    {
        scope s{ "long_jump_catch::catch" };
        longjmp(jump_buffer, 1);
    }
}


int main()
{
    normal_try_catch();
    std::cout << "----------------------------" << std::endl;
    long_jump_catch();
    return 0;
}

实际上有一个关于这个特定案例的 C++ 缺陷报告,http://wg21.link/cwg2361。所以现在还悬而未决什么是正确的。