隐藏的坑——C++中局部静态变量的构造/析构顺序问题

 要论C++中对象的构造/析构顺序,这是个“老问题”了,今番再论,实在是有感而发。

首先要明确一条原则:构造与析构的顺序是相反的。

个人总结,在这个知识点的理解和掌握上,有四层“境界”:

第一层:理解一个类内部各个数据成员之间的构造/析构顺序。这里需要注意的是,数据成员的构造顺序与其在类中声明的顺序相同,而无论构造函数中初始化列表中的顺序,这个不难;

第二层:理解基类和派生类之间的构造/析构顺序。这似乎是大部分C++书籍着墨最多的知识点,当然也不难掌握;

第三层:理解在函数内部出现的局部变量的构造/析构顺序。同上,也不难掌握;

第四层:理解在一个应用程序内部,静态变量的构造/析构顺序。其中,对于全局静态变量,视编译器的实现而定(一种方式是根据字母顺序来决定);对于有依赖关系的,那么视依赖关系而定。而对于局部静态变量来说,问题就开始复杂了。这也正是本文论述的重点。

首先,要理解编译器是如何实现局部静态变量的语法特性的。从语法上来看,局部静态变量与全局静态变量最大的不同在于构造时机——当且仅当程序执行路径首次达到局部静态变量的定义处才出发构造。注意是首次。印象中编译器是通过添加一个标识变量(当然这个变量一定是全局静态的)来实现的(即每次程序执行到时,首先检查这个标志变量),如此来确保调用时构造且只构造一次的特性。那么反过来看局部静态变量的析构,编译器会维护一个析构函数的函数指针栈,一旦构造完成,就会把相应的析构函数指针放到这个栈中。当程序结束后,由编译器生成的doexit函数会逐个调用这些析构函数,完成进程结束前的扫尾工作。基于此,很显然,对于分布在程序各处的静态局部变量,其构造顺序取决于它们在程序的实际执行路径上的先后顺序,而析构顺序则正好与之相反。

很简单,不是么?可为什么说是隐藏的坑呢,问题在于:

一方面是因为程序的实际执行路径有多个决定因素(例如基于消息驱动模型的程序和多线程程序),有时是不可预知的;

另一方面是因为局部静态变量分布在程序代码各处,彼此直接没有明显的关联,很容易让开发者忽略它们之间的这种关系(这是最坑的地方)。
 

既然提出问题,那么就讨论应对之道:

(1)最简单的,避免使用局部静态变量,将变量的声明周期控制在开发者手中;

(2)如果确有需要,那么尽量确保局部静态变量之间构造和析构是彼此独立互不相关的,换句话说,它们可以以任意的顺序被构造和析构;

嗯……暂时想到以上这些

 

 

  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值