为何GCC允许在类成员方法中用对象成员初始化静态变量?
问题分析:C++成员函数内静态变量的初始化
先看这段代码:
class MyClass { public: int obj_field; MyClass(int val) : obj_field(val) {} void myMethod() { static int my_static_var = obj_field; // <--- 为什么这是合法的? // ... } };
疑问
原本坚信这段代码不符合语法,因为认为静态变量只能用constexpr初始化(假设初始化在程序执行开始前完成),但它在GCC 9.4.0、C++17标准下编译无报错。想了解这是将静态变量初始化延迟到首次调用时执行,还是属于未定义行为?同时明白如果正常工作,静态变量的值很可能取自首个调用该方法的对象。
解答
这是C++标准明确规定的合法行为,既不是未定义行为,也确实是延迟到首次调用时初始化,具体细节如下:
函数内静态变量的初始化时机
C标准规定:函数(包括非静态成员函数)内的局部静态变量,会在首次执行到该变量的定义语句时完成初始化,而非程序启动阶段。C11及以后的标准还保证这种初始化是线程安全的,且只会执行一次。为什么可以用
obj_field初始化
调用非静态成员函数myMethod()时,必须通过一个已构造完成的MyClass对象,此时obj_field作为该对象的成员变量,已经有合法的内存空间和确定的值,用它来初始化静态变量是完全合法的表达式。静态变量的后续行为
该静态变量my_static_var只会被初始化一次,后续调用myMethod()时,这条初始化语句会被直接跳过。因此它的值确实是第一个调用myMethod()的对象的obj_field值,后续无论用哪个对象调用该方法,都不会改变它的初始值(除非代码中手动修改)。纠正误解
并非所有静态变量都要求constexpr初始化:- 全局/命名空间作用域的静态变量,若要在程序启动时(编译/链接阶段)完成初始化,才需要
constexpr表达式;若用非constexpr表达式,会在运行期的动态初始化阶段完成。 - 而函数内的局部静态变量,本身就设计为延迟初始化,允许使用任意合法的表达式初始化,只要初始化时表达式的求值是合法的。
- 全局/命名空间作用域的静态变量,若要在程序启动时(编译/链接阶段)完成初始化,才需要
内容的提问来源于stack exchange,提问作者Audrius Meškauskas
相关产品推荐
相关产品推荐

