内联变量的初始化销毁顺序、实例化规则及相关问题问询
C++内联变量常见问题解答
问题背景
用户给出了依赖内联变量的代码示例,发现未被引用的内联变量也会被实例化,并提出四个相关问题。
全局内联变量示例
#include <memory> #include <iostream> struct A { A() : p(std::make_shared<int>(5)) {} std::shared_ptr<int> p; }; struct B { B(A& a) : p(a.p.get()) { func(); } ~B() { func(); } void func() { std::cout << *p; } int* p; }; inline A a; inline B b(a); int main() { return 0; }
类内静态内联变量示例
class C { public: static inline A a; static inline B b = B(a); };
问题解答
1. 是否能保证b在a之后创建、在a之前销毁?
可以保证,分两种场景:
- 同一翻译单元的全局inline变量:C++标准规定,同一翻译单元内的全局对象初始化顺序遵循声明顺序,销毁顺序则与初始化顺序相反。因此
a会先于b构造,b会先于a销毁,完全匹配依赖关系。 - 类内静态inline变量:同一类中的静态inline变量,初始化顺序严格遵循类内的声明顺序,销毁顺序同样反向。因此
C::a先构造,C::b后构造;销毁时C::b先执行析构,C::a后执行。
2. 内联变量是否即使未被引用也总会被实例化?
通常情况下是的。C++标准要求inline变量的定义必须存在于每个用到它的翻译单元中,且编译器需确保其唯一实例化。即使代码中没有显式引用该变量,编译器一般也会完成初始化(正如你示例中输出55的情况)。不过极端优化场景下,编译器可能会移除未被使用的inline变量,但这种情况非常少见,且不符合常规编译行为。
3. 第一个代码示例中移除inline会有哪些变化?
移除inline后,a和b变为普通全局变量,主要变化有三点:
- 链接属性:普通全局变量是外部链接,若此代码被多个翻译单元包含,会触发多重定义错误(违反ODR规则);而inline变量允许在多个翻译单元中定义,最终合并为单一实体。
- 实例化行为:普通全局变量若未被ODR使用(即代码中没有直接引用它),编译器大概率会优化掉其初始化操作,你的代码将不会输出
55。 - 跨TU风险:如果仅在单个翻译单元中使用,初始化顺序仍遵循声明顺序,但一旦跨翻译单元使用,会引入未定义行为风险。
4. 若a和b处于不同翻译单元会发生什么?
分两种情况讨论:
- 均为inline变量:不同翻译单元中的inline变量会被视为同一实体,但跨翻译单元的全局对象初始化顺序是未指定的。这意味着
b可能在a完成初始化前就被构造,导致b.p指向未初始化的a.p,触发运行时崩溃或未定义行为。 - 均为非inline变量:首先会面临链接问题——若
b所在TU未声明a为外部变量,会出现未定义符号错误;即使声明正确,跨TU的全局变量初始化顺序依然未指定,同样会导致b依赖的a未完成初始化,引发未定义行为。
内容的提问来源于stack exchange,提问作者Alexey Starinsky
相关产品推荐
相关产品推荐

