从静态变量引用非静态变量会使非静态变量先于静态变量进行析构函数

Referencing non-static variables from a static variable makes non-static variables destruct before static variable

本文关键字:静态 变量 析构函数 引用 于静态      更新时间:2023-10-16

简单地说:每当程序退出时,我都想写入一个文件,所以我在构造函数中有一个接受文件名(char*)和对Google Protobuf消息的引用的对象(出于这些目的,你可以假装它是一个字符串&),然后将消息写入析构函数中的文件名。然后,在main()中,我初始化其中一个对象,并将其声明为静态对象(因此,当程序出于任何原因退出时,它都会销毁)。

在我更改了一些(看似)无关的内容之前,我的代码在几次修订中都运行得很好,现在它不起作用了。现在,当对象析构函数时,char*和reference都指向char和Message的未集成版本。我有下面的相关代码:

using namespace std;
class WriteOnShutdown {
    private:
        const char* filename;
    public:
        MathHelper::Log& message;
        WriteOnShutdown(char* a, MathHelper::Log& b) : filename(a), message(b) {}
        ~WriteOnShutdown() {
            cout << "debugn";
            //*filename is -52 (unitialised char)
            //READ ACCESS VIOLATION - message is still valid, but message.DebugString tries to call a pointer which hasn't been initialised yet
            cout << message.DebugString() << endl; 
        }
};
int main() {
    char filename[100];
    MathHelper::Log log;
    //Initialise filename and log
    static WriteOnShutdown write(filename, log);
    //Do program stuff here
    //Then, at the end of main(), printing write.message.DebugString() works like a charm
    cout << endl << write.message.DebugString();
    _getch();
}

出现问题的原因如下:

MathHelper::Log log将在您的main返回之前被析构函数,但在main返回之后,WriteOnShutdown的析构函数将被调用

由于WriteOnShutdown在析构函数中使用对log的引用,因此您正在访问一个"悬空"引用,调用未定义的行为,从而发现问题。

filename也有同样的问题。

显而易见的解决方案是将write(顺便说一句,由于多种原因,它对对象来说是一个糟糕的名称)从静态更改为自动变量。

这被称为静态初始化顺序惨败(同样适用于析构函数)。这一直是个问题,你只是碰巧早些时候运气好。

在您的情况下,解决此问题的一个简单方法是将const char* filename;更改为std::string &filename;,将MathHelper::Log& message;更改为MathHelper::Log message;。这样,当析构函数被调用时,内存仍然存在。