【问题标题】:Guaranteeing correct initialization order of global variables保证全局变量的正确初始化顺序
【发布时间】:2012-07-12 13:08:29
【问题描述】:

我已经实现了代表枚举可能性的自定义类,类似于this article

// Color.h

class Color {
public:
    static const Color Red;
    static const Color Green;
    static const Color Blue;
    //...
private:
    explicit Color(int code);
    //...

    int code;
    static std::set<Color> colors;
};

// Color.cpp:

const Color Color::Red(1);
const Color Color::Green(2);
const Color Color::Blue(3);    
//...

现在,当我想使用 Color 的实例时遇到问题,例如:Color::Red 在另一个翻译单元中初始化全局变量。我知道发生这种情况是因为没有定义首先初始化哪个翻译单元全局变量。如何解决初始化排序问题?

我能想到的唯一解决方案是使用漂亮的计数器。但我不知道如何在不影响枚举类语法的情况下使用它。我正在考虑将set() 方法添加到Color 中。然后我可以在一个漂亮的计数器中调用这个方法,比如初始化器:

// Color.h

class Color {
public:
    void set(int code);
    //...
private:
    Color() { /* empty */}
    //...
};

static class ColorInitializer {
    ColorInitializer () {
        static bool initialized = false;
        if(initialized)
            return;

        Color::Red.set(1);
        Color::Green.set(1);
        Color::Blue.set(1);
        initialized = true;
    }
} colorInitializer;

// Color.cpp

const Color Color::Red;
const Color Color::Green;
const Color Color::Blue;

但我在这里看到的问题是 set 方法可以在尚未构造的对象上调用。可以吗,还是行为未定义?如何更好地解决未定义初始化顺序的问题?

【问题讨论】:

  • 我没有使用 C++11 的经验,但我严重怀疑使用 ColorInitializer 的整个方法并不好。更好地调查使用 {...} 等进行初始化。您的第一个代码示例遇到什么错误?
  • @Kirill Kobelev:我没有收到任何编译错误,这只是一个未定义的行为。变量在使用时可能会或可能不会被初始化。
  • 那么我怀疑这种行为与您的特定班级无关。尝试隔离问题。链接器有问题?
  • 保证正确初始化顺序的最简单方法是不使用任何全局变量。
  • @Frerich Raabe:如您所见,我的接受率相当高。这意味着我知道如何接受答案。

标签: c++


【解决方案1】:

在 C++11 中(如果你负担得起的话)你可以使用扩展的常量表达式功能:

class Color {
public:
    static constexpr const Color Red;
    static constexpr const Color Green;
    static constexpr const Color Blue;
private:
    constexpr explicit Color(int code);
    //...
};

// Color.cpp:

constexpr Color Color::Red(1);
constexpr Color Color::Green(2);
constexpr Color Color::Blue(3);    
//...

【讨论】:

  • 谢谢,不幸的是,这是在一个旧项目中使用的,至少现在 C++11 不是一个选项。
【解决方案2】:

首先不要使用全局对象。相反,使用函数:

class Color {
public:
    static const Color &Red() {
      static const Color c( 1 );
      return c;
    }

    static const Color &Green() {
      static const Color c( 2 );
      return c;
    }

    static const Color &Blue() {
      static const Color c( 3 );
      return c;
    }
private:
    explicit Color(int code);
    //...
};

您现在可以使用这些函数来初始化其他对象;调用它们将确保调用 Color 对象的构造函数。

【讨论】:

【解决方案3】:

由于您的 Color 对象是轻量级的,因此只能使用静态函数。

class Color {
public:
    static Color Red() { return Color(1); }
    static Color Green() { return Color(2); }
    static Color Blue() { return Color( 3 ); }
private:
    explicit Color(int code);
};

唯一的问题是你不能将这些对象传递给需要指针或非常量引用的函数(但在这种情况下非常量引用可能是无意义的)。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-01-12
    • 1970-01-01
    • 2019-04-15
    • 1970-01-01
    相关资源
    最近更新 更多