从静态变量引用非静态变量会使非静态变量在静态变量之前析构

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

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

在我更改一些(看似)不相关的内容之前,我的代码在多次修订后一直运行良好,但现在它不起作用了。现在,当对象析构时,char* 和引用都指向 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 << "debug\n";
            //*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();
}

这称为 static initialization order fiasco(同样适用于析构函数)。一直都是问题,只是运气早点而已。

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

您遇到问题的原因如下:

MathHelper::Log log 将在 之前 您的主要 returns 被销毁,但 WriteOnShutdown 的析构函数将被调用 之后 主要returns.

由于 WriteOnShutdown 在析构函数中使用对 log 的引用,您正在访问 'dangling' 引用,调用未定义的行为,因此看到了问题。

你也遇到了与 filename 完全相同的问题。

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