3

我见过几个类似的代码片段,如下所示:

struct MyExcept : std::exception {
    explicit MyExcept(const char* m) noexcept : message{m} {}

    const char* what() const noexcept override {
        return message;
    }

    const char* message;
};

void foo() {
    std::string error;

    error += "Some";
    error += " Error";

    throw MyExcept{error.c_str()};
}

int main() {
    try {
        foo();
    } catch (const MyExcept& e) {
        // Is this okay?
        std::cout << e.message << std::endl;
    }
}

在注释之后的行中Is this okay?,我们读取了在foo函数中使用分配的 c 样式字符串std::string。由于字符串因堆栈展开而被破坏,这是未定义的行为吗?


如果它确实是未定义的行为,如果我们main用这个替换函数呢?

int main() {
    foo();
}

由于没有捕获,编译器不会强制展开堆栈,而是what()在控制台中输出结果并中止程序。那么它仍然是未定义的行为吗?

4

3 回答 3

5

是的,这是未定义的行为。您正在使用悬空指针。

void foo() {
    std::string error;

    error += "Some";
    error += " Error";

    throw MyExcept{error.c_str()};
} // <<  error goes out of scope here and so does the pointer returned
  //     from c_str()

由于没有捕获,编译器不会强制展开堆栈,而是what()在控制台中输出结果并中止程序。那么它仍然是未定义的行为吗?

由于默认实现将使用std::terminate并反过来调用std::abort()this 可能仍然是未定义的行为,因为大多数标准处理程序实现将尝试取消引用what().

您可以安装自己的处理程序来避免这种情况。

于 2017-07-20T16:13:19.877 回答
2

您的第一个片段具有未定义的行为。[exception.ctor]/1

当控制从抛出异常的点传递到处理程序时,析构函数被本节中指定的进程调用,称为堆栈展开。

在这里,析构函数 orerror被调用,导致c_str()成为一个悬空指针。例如,当您使用它时,稍后取消引用它std::cout是未定义的行为。

你的第二个片段非常好。没有理由说它是未定义的行为。你从来没有真正调用过what,或者做任何其他可能导致未定义行为的事情。唯一没有被标准定义的是堆栈展开是否发生,[except.terminate]/2

在没有找到匹配处理程序的情况下,在调用之前堆栈是否展开是实现定义的std​::​terminate()

于 2017-07-20T16:36:09.663 回答
1

正如其他人所说,代码具有未定义的行为,因为分配给的指针message 是悬空的。

std::runtime_error已经为这个问题提供了解决方案。调用以 astd::string作为输入的构造函数,并且根本不覆盖what()

struct MyExcept : std::runtime_error {
    explicit MyExcept(const std::string & m) noexcept : std::runtime_error(m) {}
};

void foo() {
    std::string error;

    error += "Some";
    error += " Error";

    throw MyExcept(error);
}

int main() {
    try {
        foo();
    }
    catch (const MyExcept& e) {
        std::cout << e.what() << std::endl;
    }
}

std::runtime_error有一个内部std::string数据what()默认返回,从而避免了悬空问题。

于 2017-07-20T17:54:19.580 回答