简体   繁体   English

是静态常量字符串成员变量总是在使用前初始化?

[英]is static const string member variable always initialized before used?

In C++, if I want to define some non-local const string which can be used in different classes, functions, files, the approaches that I know are: 在C ++中,如果我想定义一些可以在不同类,函数,文件中使用的非本地const字符串 ,那么我知道的方法是:

  1. use define directives, eg 使用定义指令,例如

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

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

     class Demo{ public: static const std::string GetValue(){return "some_string_value";} }; 

Now what I am not clear is, if we use the 2nd approach, is the variable ConstStrVal always initialized to "some_string_value" before it is actually used by any code in any case? 现在,我不清楚的是,如果使用第二种方法,在任何情况下,任何代码实际上都不会将变量ConstStrVal初始化为“ some_string_value”吗? Im concerned about this because of the "static initialization order fiasco" . 我担心这是因为“静态初始化顺序惨败” If this issue is valid, why is everybody using the 2nd approach? 如果此问题有效,为什么每个人都使用第二种方法?

Which is the best approach, 2 or 3? 最好的方法是2还是3? I know that #define directives have no respect of scope, most people don't recommend it. 我知道#define指令不涉及范围,大多数人不建议这样做。

Thanks! 谢谢!

if we use the 2nd approach, is the variable ConstStrVal always initialized to "some_string_value" before it is actually used by any code in any case? 如果我们使用第二种方法,那么在任何情况下,任何代码都不会实际将变量ConstStrVal初始化为“ some_string_value”吗?

No 没有

It depends on the value it's initialized to, and the order of initialization. 它取决于初始化的值以及初始化的顺序。 ConstStrVal has a global constructor. ConstStrVal具有全局构造函数。

Consider adding another global object with a constructor: 考虑使用构造函数添加另一个全局对象:

static const std::string ConstStrVal2(ConstStrVal);

The order is not defined by the language, and ConstStrVal2 's constructor may be called before ConstStrVal has been constructed. 顺序不是由语言定义,并且ConstStrVal2之前的构造可称为ConstStrVal已经构造。

The initialization order can vary for a number of reasons, but it's often specified by your toolchain. 初始化顺序可能由于多种原因而有所不同,但是通常由您的工具链指定。 Altering the order of linked object files could (for example) change the order of your image's initialization and then the error would surface. 更改链接的目标文件的顺序可能(例如)更改图像初始化的顺序,然后错误就会浮出水面。

why is everybody using the 2nd approach? 为什么每个人都使用第二种方法?

many people use other approaches for very good reasons… 许多人出于很好的理由而使用其他方法…

Which is the best approach, 2 or 3? 最好的方法是2还是3?

Number 3. You can also avoid multiple constructions like so: 数字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;
  }  
};  

caution: this is approach is still capable of the initialization problem seen in ConstStrVal2(ConstStrVal) . 警告:这种方法仍然能够解决ConstStrVal2(ConstStrVal)看到的初始化问题。 however, you have more control over initialization order and it's an easier problem to solve portably when compared to objects with global constructors. 但是,您可以更好地控制初始化顺序,与具有全局构造函数的对象相比,这是一个更容易解决的问题,可移植。

In general, I (and many others) prefer to use functions to return values rather than variables, because functions give greater flexibility for future enhancement. 通常,我(以及许多其他人)更喜欢使用函数而不是变量来返回值,因为函数为将来的增强提供了更大的灵活性。 Remember that most of the time spent on a successful software project is maintaining and enhancing the code, not writing it in the first place. 请记住,在成功的软件项目上花费的大部分时间都是在维护和增强代码,而不是首先编写代码。 It's hard to predict if your constant today might not be a compile time constant tomorrow. 很难预测今天的常量是否可能不是明天的编译时间常量。 Maybe it will be read from a configuration file some day. 也许有一天会从配置文件中读取它。

So I recommend approach 3 because it does what you want today and leaves more flexibility for the future. 因此,我建议使用方法3,因为它可以满足您今天的需求,并为将来留出更多的灵活性。

Avoid using the preprocessor with C++. 避免将预处理器与C ++一起使用。 Also, why would you have a string in a class, but need it in other classes? 另外,为什么在一个类中有一个字符串,而在其他类中却需要它呢? I would re-evaluate your class design to allow better encapsulation. 我将重新评估您的类设计,以实现更好的封装。 If you absolutely need this global string then I would consider adding a globals.h/cpp module and then declare/define string there as: 如果您绝对需要此全局字符串,则可以考虑添加一个globals.h / cpp模块,然后在其中声明/定义字符串为:

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

Don't use preprocessor directives in C++, unless you're trying to achieve a holy purpose that can't possibly be achieved any other way. 不要在C ++中使用预处理程序指令,除非您试图实现一个神圣的目标,而这是其他任何方式都不可能实现的。

From the standard (3.6.2): 从标准(3.6.2):

Objects with static storage duration (3.7.1) shall be zero-initialized (8.5) before any other initialization takes place. 具有静态存储持续时间(3.7.1)的对象应在进行任何其他初始化之前进行零初始化(8.5)。 A reference with static storage duration and an object of POD type with static storage duration can be initialized with a constant expression (5.19); 可以使用常量表达式(5.19)初始化具有静态存储持续时间的引用和具有静态存储持续时间的POD类型的对象。 this is called constant initialization. 这称为常量初始化。 Together, zero-initialization and constant initialization are called static initialization; 零初始化和常量初始化合在一起称为静态初始化。 all other initialization is dynamic initialization. 所有其他初始化是动态初始化。 Static initialization shall be performed before any dynamic initialization takes place. 静态初始化应在任何动态初始化发生之前执行。 Dynamic initialization of an object is either ordered or unordered. 对象的动态初始化是有序的或无序的。 Definitions of explicitly specialized class template static data members have ordered initialization. 显式专门化的类模板静态数据成员的定义已进行了初始化。 Other class template static data members (ie, implicitly or explicitly instantiated specializations) have unordered initialization. 其他类模板静态数据成员(即,隐式或显式实例化的专长)具有无序初始化。 Other objects defined in namespace scope have ordered initialization. 在命名空间范围内定义的其他对象已按顺序进行初始化。 Objects defined within a single translation unit and with ordered initialization shall be initialized in the order of their definitions in the translation unit. 在单个翻译单元中定义并进行有序初始化的对象应按照其在翻译单元中定义的顺序进行初始化。 The order of initialization is unspecified for objects with unordered initialization and for objects defined in different translation units. 对于具有无序初始化的对象以及在不同转换单元中定义的对象,未指定初始化的顺序。

So, the fate of 2 depends on whether your variable is static initialised or dynamic initialised. 因此,命运2取决于您的变量是静态初始化还是动态初始化。 For instance, in your concrete example, if you use const char * Demo::ConstStrVal = "some_string_value"; 例如,在您的具体示例中,如果使用const char * Demo::ConstStrVal = "some_string_value"; (better yet const char Demo::ConstStrVal[] if the value will stay constant in the program) you can be sure that it will be initialised no matter what. (更好的是const char Demo::ConstStrVal[]如果该值在程序中保持不变),您可以确保无论如何都会对其进行初始化。 With a std::string , you can't be sure since it's not a POD type (I'm not dead sure on this one, but fairly sure). 使用std::string ,您不能确定,因为它不是POD类型(我不确定这一点,但可以肯定)。

3rd method allows you to be sure and the method in Justin's answer makes sure that there are no unnecessary constructions. 第三种方法使您可以确定,并且Justin答案中的方法可确保没有不必要的构造。 Though keep in mind that the static method has a hidden overhead of checking whether or not the variable is already initialised on every call. 尽管要记住,静态方法具有检查每个调用是否已初始化变量的隐藏开销。 If you're returning a simple constant, just returning your value is definitely faster since the function will probably be inlined. 如果您要返回一个简单的常量,则仅返回值肯定会更快,因为该函数可能会内联。

All of that said, try to write your programs so as not to rely on static initialisation. 综上所述,请尝试编写程序,以免依赖静态初始化。 Static variables are best regarded as a convenience, they aren't convenient any more when you have to juggle their initialisation orders. 最好将静态变量视为一种便利,当您必须处理它们的初始化顺序时,它们就不再方便了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM