是静态const字符串成员变量在使用之前总是初始化的吗?

时间:2012-04-21 05:39:43

标签: c++ const-string

在C ++中,如果我想定义一些非本地const字符串,可以在不同的类,函数,文件中使用,我知道的方法是:

  1. 使用define指令,例如

    #define STR_VALUE "some_string_value"
    
  2. const类成员变量,例如

    class Demo {  
    public:  
      static const std::string ConstStrVal;  
    };  
    // then in cpp  
    std::string Demo::ConstStrVal = "some_string_value";  
    
  3. const类成员函数,例如

    class Demo{  
    public:  
      static const std::string GetValue(){return "some_string_value";}  
    };  
    
  4. 现在我不清楚的是,如果我们使用第二种方法,变量ConstStrVal总是在任何情况下被任何代码实际使用之前始终初始化为“some_string_value”?我因为"static initialization order fiasco"而对此表示担忧。如果这个问题有效,为什么每个人都使用第二种方法?

    哪种方法最好,2或3?我知道#define指令不考虑范围,大多数人不推荐它。

    谢谢!

4 个答案:

答案 0 :(得分:5)

  

如果我们使用第二种方法,变量ConstStrVal在任何情况下被任何代码实际使用之前总是初始化为“some_string_value”吗?

它取决于它初始化的值,以及初始化的顺序。 ConstStrVal有一个全局构造函数。

考虑使用构造函数添加另一个全局对象:

static const std::string ConstStrVal2(ConstStrVal);

订单不是由语言定义的,ConstStrVal2的构造函数可能在构造ConstStrVal之前被调用。

初始化顺序可能因多种原因而有所不同,但通常由工具链指定。改变链接对象文件的顺序可以(例如)改变图像初始化的顺序,然后错误就会出现。

  

为什么每个人都使用第二种方法?

许多人出于非常好的理由使用其他方法......

  

哪种方法最好,2或3?

编号3.您还可以避免多种结构:

class Demo {
public:  
  static const std::string& GetValue() {
    // this is constructed exactly once, when the function is first called
    static const std::string s("some_string_value");
    return s;
  }  
};  

警告:这种方法仍然能够解决ConstStrVal2(ConstStrVal)中出现的初始化问题。但是,您可以更好地控制初始化顺序,与具有全局构造函数的对象相比,这是一个更容易解决的问题。

答案 1 :(得分:1)

通常,我(和许多其他人)更喜欢使用函数来返回值而不是变量,因为函数为将来的增强提供了更大的灵活性。请记住,花在成功的软件项目上的大部分时间是维护和增强代码,而不是首先编写代码。很难预测今天你的常数是否可能不是明天的编译时间常数。也许某天会从配置文件中读取它。

因此,我建议采用方法3,因为它可以满足您的需求,并为未来提供更多灵活性。

答案 2 :(得分:0)

避免将预处理器与C ++一起使用。另外,为什么你会在一个类中有一个字符串,但在其他类中需要它?我会重新评估你的类设计,以便更好地封装。如果你绝对需要这个全局字符串,那么我会考虑添加一个globals.h / cpp模块,然后在那里声明/定义字符串:

const char* const kMyErrorMsg = "This is my error message!";

答案 3 :(得分:0)

不要在C ++中使用预处理程序指令,除非你试图达到一个不可能以任何其他方式实现的神圣目的。

从标准(3.6.2):

  

具有静态存储持续时间(3.7.1)的对象应进行零初始化   (8.5)在进行任何其他初始化之前。参考用   静态存储持续时间和具有静态存储的POD类型的对象   持续时间可以用常量表达式初始化(​​5.19);这是   称为常量初始化。零初始化和   常量初始化称为静态初始化;所有其他   初始化是动态初始化。静态初始化应该   在任何动态初始化发生之前执行。动态   对象的初始化是有序的还是无序的。   显式专用类模板静态数据的定义   成员已订购初始化。其他类模板静态数据   成员(即隐式或显式实例化的专业化)   有无序的初始化。命名空间中定义的其他对象   范围已订购初始化。单个内定义的对象   翻译单元和有序初始化应初始化   按翻译单位中的定义顺序排列。命令   对于具有无序的对象,未指定初始化   初始化和在不同翻译单元中定义的对象。

因此,2的命运取决于您的变量是静态初始化还是动态初始化。例如,在您的具体示例中,如果您使用const char * Demo::ConstStrVal = "some_string_value";(如果值在程序中保持不变,则更好const char Demo::ConstStrVal[]),无论如何都可以确保它将被初始化。使用std::string,你不能确定它是不是POD类型(我不确定这个,但相当肯定)。

第三种方法可以让你确定,Justin的答案中的方法确保没有不必要的结构。但请记住,静态方法有一个隐藏的开销,即检查变量是否已在每次调用时初始化。如果你返回一个简单的常量,那么只返回你的值肯定会更快,因为函数可能会被内联。

所有这些都说,尝试编写程序,以免依赖静态初始化。静态变量最好被认为是一种方便,当你不得不兼顾它们的初始化命令时,它们就不再方便了。