是否存在使用std::set_terminate无法捕获C++纯虚拟函数调用的情况

Are there cases where using std::set_terminate does not catch a C++ pure virtual function call?

本文关键字:C++ 虚拟 情况 函数调用 存在 std set terminate 是否      更新时间:2023-10-16

在Linux服务器上运行的可执行文件的深处,有一个C++纯虚拟函数调用。它导致服务器崩溃,没有留下程序跟踪数据、堆栈跟踪和核心转储。只剩下一些日志文件。几乎可以肯定,这是由一生的问题引起的,我很清楚它发生在哪里,但我想要证据。我试图通过使用std::set_terminate设置一个处理程序来纠正这种情况,该处理程序在调用terminate时运行。这在测试中有效。例如,如果我引起一个纯虚拟呼叫,使用:

class Base {
public:
    Base() {}
    virtual ~Base(){}
    virtual void foo() = 0;
};
class Derived: public Base {
public:
    Derived(): n_(0) {}
    ~Derived(){}
    void foo() {
            n_ = 1;
    }
private:
    int n_;
};              

然后

Base* p = new Derived();
Base* p1 = p;
p->~Base();
p1->foo();

处理程序工作并生成跟踪数据、堆栈跟踪和核心转储。运行时系统打印

称为的纯虚拟方法

用于此测试。

代码中没有其他对set_terminate的调用。测试是在服务器启动并运行时完成的,因此任何可能会占用我的处理程序的后续调用都应该发生,如果它们要发生的话。然而,在实际的服务器中,这确实而不是捕获纯虚拟调用。我能想到的唯一可能导致这种情况是在我的调用之后设置终止处理程序。有没有其他方法可以避免我的终止处理程序?

实际上,如果您有未定义的行为,对执行环境或编译的二进制文件的任何更改都可能导致代码以不同的方式出现错误。

这在更改优化和调试标志时尤其明显,但看似无害的代码更改也会引发这种情况。

接下来,您的错误可能是一个heisenbug,意思是与定时/多线程有关吗?

此外,如果在构建/销毁中不需要虚拟表,并且实际上从未创建过确切类型的对象,那么编译器可能会完全省略这些虚拟表
因此,对foo的调用可能同样只是崩溃或调用derived::foo,尽管析构函数已经运行。