是静态常量字符串成员变量,始终在使用前初始化
is static const string member variable always initialized before used?
在C++中,如果我想定义一些可以在不同的类、函数、文件中使用的非本地 const 字符串,我知道的方法有:
-
使用定义指令,例如
#define STR_VALUE "some_string_value"
-
常量类成员变量,例如
class Demo { public: static const std::string ConstStrVal; }; // then in cpp std::string Demo::ConstStrVal = "some_string_value";
-
常量类成员函数,例如
class Demo{ public: static const std::string GetValue(){return "some_string_value";} };
现在我不清楚的是,如果我们使用第二种方法,变量 ConstStrVal 是否总是在被任何代码实际使用之前初始化为"some_string_value"?由于"静态初始化顺序惨败",我对此感到担忧。如果这个问题是有效的,为什么每个人都使用第二种方法?
哪种是最好的方法,2 还是 3?我知道 #define 指令不尊重范围,大多数人不推荐它。
谢谢!
如果我们使用第二种方法,变量 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)
中看到的初始化问题。 但是,您可以更好地控制初始化顺序,与具有全局构造函数的对象相比,这是一个更容易可移植解决的问题。
一般来说,我(和许多其他人)更喜欢使用函数而不是变量来返回值,因为函数为将来的增强提供了更大的灵活性。 请记住,花在一个成功的软件项目上的大部分时间是维护和增强代码,而不是首先编写代码。 很难预测今天的常量明天是否可能不是编译时常量。 也许有一天它会从配置文件中读取。
所以我推荐方法 3,因为它可以满足你今天想要的效果,并为未来留出更大的灵活性。
避免将预处理器与 C++一起使用。 另外,为什么你会在一个类中有一个字符串,但在其他类中需要它? 我会重新评估您的类设计,以便更好地封装。 如果你绝对需要这个全局字符串,那么我会考虑添加一个 globals.h/cpp 模块,然后在那里声明/定义字符串为:
const char* const kMyErrorMsg = "This is my error message!";
不要在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 类型(我对此不太确定,但相当确定)。
第三种方法可以让你确定,贾斯汀答案中的方法确保没有不必要的结构。但请记住,静态方法有一个隐藏的开销,即检查变量是否已在每次调用时初始化。如果你返回一个简单的常量,只返回你的值肯定更快,因为该函数可能会被内联。
综上所述,尝试编写程序,以免依赖静态初始化。静态变量最好被视为一种方便,当您必须处理它们的初始化顺序时,它们不再方便。
- 无法在声明时使用初始值设定项列表初始化常量字符*/字符串数组的向量
- 初始化常量字符* 数组
- 为什么MSVC14允许声明指向动态未初始化常量对象的指针
- C++初始化常量
- 在可变参数构造函数中初始化常量数组
- 使用数字初始化常量引用
- 如何在不初始化常量的情况下声明数组?
- 初始化常量静态 std::map unique_ptr作为值
- 为什么您可以初始化常量引用,但不能初始化来自右值的非常量引用
- 类中的初始化常量
- 如何初始化常量 CLSID
- 在类中初始化常量数组
- 在声明C++之后初始化常量变量
- C++初始化:常量全局与静态类成员的顺序
- 在头文件中声明并初始化常量
- 恒常性添加无效?错误:无法使用字符**初始化常量字符**
- 为什么我能够在构造函数中初始化常量
- 通过指针初始化常量矩阵<双精度,0,80>(来自 Dlib)
- C++构造函数:初始化常量引用时出现垃圾
- 用自身初始化C++常量变量