c++中不一致的文件作用域变量行为

Inconsistent file-scoped variable behavior in C++

本文关键字:变量 作用域 文件 不一致 c++      更新时间:2023-10-16

标题可以改进,但我不确定问题是什么。欢迎提出建议。我有一个自定义枚举类,Color:

// Color.h
class Color {
protected:
    int id;
    Color(int id) : id(id) {}
    void operator&(); //undefined
public:
    Color(const Color& r) : id(r.id) {}
    Color& operator=(const Color& r) {id=r.id; return *this;}
    bool operator==(const Color& r) const {return id==r.id;}
    bool operator!=(const Color& r) const {return id!=r.id;}
    operator int() const {return id;} //so you can still switch on it
    static Color Blue;          
    static Color Red;           
};
// Color.cpp
#include "Color.h"
Color Color::Blue(0);
Color Color::Red(1);

main中,我有一个Colors的文件作用域数组。当我在文件作用域数组中打印Colors的值时,一切都是正确的:

// main.cpp
#include "Test.h"
using namespace std;
const Color mainColors[] = {Color::Red, Color::Red }; // values should be [1, 1]
int main()
{
    cout << "Main file-scoped colors: " << mainColors[0] << ", " << mainColors[1] << endl; // prints [1, 1]
    Test();
    return 0;
}

然而,如果我尝试在另一个文件Test中做同样的事情,它不起作用。当我打印文件作用域数组时,值是不正确的,就好像它们没有初始化一样。:

// Test.h
#include "Color.h"
void Test();
// Test.cpp
#include "Test.h"
const Color fooColors[] = {Color::Red, Color::Red}; // values should be [1, 1]
void Test()
{
    cout << "Test file-scoped colors: " << fooColors[0] << ", " << fooColors[1] << endl; // prints [0, 0]
}

我在这里做错了什么?我认为这可能与枚举成员是静态的有关,但我不能把我的手指放在上面。

您看到的问题是静态变量初始化顺序未指定的结果。

行为可以很容易地改变构建系统的细微变化(例如调试与非调试,添加另一个文件到程序,等等)

看起来main.cpp中的mainColorsColor::RedColor::Blue初始化之后才被初始化,Test.cpp中的fooColorsColor::RedColor::Blue初始化之前被初始化。

变通(感谢@Andy)

增加static成员函数Blue()Red()Color。使用它们来初始化main.cppTest.cpp中的变量

In Color.h:

static Color Blue();
static Color Red();

In Color.cpp:

Color Color::Blue() {return Color(0);}
Color Color::Red() {return Color(1);}

In main.cpp:

const Color mainColors[] = {Color::Blue(), Color::Red() };

In Test.cpp:

const Color fooColors[] = {Color::Blue(), Color::Red()};

这与枚举成员是静态的有关。在翻译单元中,静态成员数据的初始化顺序是未定义的。基本上,第一次调用(main)是幸运的,而第二次调用(Test.cpp)是不幸的。有几种方法可以解决这个问题,但这是最重要的。

在这个特殊的情况下,Test fooColors首先使用Color类中的未初始化的静态数据进行初始化。这个数据然后被初始化,然后最后mainColors从Color::red和Color::blue中初始化。