#pragma init_seg
先进后出原则,最先初始化的最后析构!
1.C++中全局对象、变量的构造函数调用顺序是跟声明有一定关系的,即在同一个文件中先声明的先调用。对于不同文件中的全局对象、变量,它们的构造函数调用顺序是未定义的,取决于具体的编译器
2.C++总是按成员变量在类声明中出现的顺序来初始化成员变量的,为什么C++不按初始化列表的顺序来初始化成员变量呢?因为我们知道初始化的顺序应该与析构的顺序相反,而对一个类来说 constructor 可能有多个,初始化列表也会有多个,所以C++就选择了简单的点的方法,按成员变量出现的顺序来初始化。
3.基类的静态变量先初始化,然后是它的派生类。直到所有的静态变量都被初始化。这里需要注意全局变量和静态变量的初始化是不分次序的。这也不难理解,其实静态变量和全局变量都被放在公共内存区。可以把静态变量理解为带有“作用域”的全局变量
顺序为: 1基类的静态变量或全局变量 2派生类的静态变量或全局变量 3基类的成员变量 4派生类的成员变量
C++标准中,处于同一编译单元(cpp)的全局对象按其声明次序初始化并倒序析构,但标准中没有说处于不同编译单元的全局对象的初始化顺序。这带来了很多问题。
假如有个Log对象负责程序日志的记录。如果程序结束时,有某个全局对象出现类似于资源释放失败的错误,该对象会调用Log记录错误,这时,Log可能已经被销毁了…… 这就是所谓的dead-reference问题。
本篇介绍的#pragama init_seg 预处理器指令提供了一种解决方案,在Effective C++ —— 让自己习惯C++(一)条款03中提供了另一种方案。
// pragma_directive_init_seg.cpp
#include <stdio.h>
#pragma warning(disable : 4075)
//定义一个保存全局对象析构函数的指针的数组,供程序结束前显示调用来销毁全局对象:
typedef void (__cdecl *PF)(void); //析构函数必须是无参数、无返回值
int cxpf = ; // 我们需要调用的析构函数的数量
PF pfx[]; // 保存析构函数的指针的数组.
//编译器调用该函数,把pragma init_seg语句作用范围内的全局变量的析构函数全部登记入dtor函数指针数组
int myexit (PF pf) {
pfx[cxpf++] = pf;
return ;
}
//全局对象的类型定义
struct A
{
A(int i)
{
value=i;
printf("A():%d\n",i);
}
~A()
{
printf("~A():%d\n",value);
}
int value;
};
// 由于在pragma init_seg语句之前,这个全局变量的ctor & dtor仍然由CRT启动代码来调用,
/* CRT startup code,就是在main函数(或者WinMain函数)之前和之后执行的代码,
完成运行环境初始化与事后的清理释放工作*/
A aaaa(); //下述四行语句,模仿了编译器与链接器默认构建全局变量初始化的.CRT的开头和结尾函数指针的行为方式,
//构建了用户自己的.mine节来保存全局变量初始化函数指针表的开头和结尾。
//节(Section)名字必须不多于8个字符;
//在$号之前具有相同字符串的节,将被链接器合并为同一个节,合并顺序依照$号之后字符串的字典序;
// InitSegStart与InitSegEnd作为边界值,使得我们可以发现那些真正的需被调用的全局变量初始化函数
#pragma section(".mine$a", read)
__declspec(allocate(".mine$a")) const PF InitSegStart = (PF);
#pragma section(".mine$z",read)
__declspec(allocate(".mine$z")) const PF InitSegEnd = (PF); // InitializeObjects是由程序显示调用执行,来初始化pragma init_seg语句作用下的那些全局变量
// 编译时,每个节是256个字符。链接器合并这些节时,以256个字节为单位,对于空闲的字节填入0
// 所以调用函数指针前,必须与0值做比较,排除这些0值
void InitializeObjects ()
{
const PF *x = &InitSegStart;
for (++x ; x < &InitSegEnd ; ++x)
if (*x) (*x)();
}
//DestroyObjects由程序显示调用执行,销毁pragma init_seg语句作用下的那些全局变量
void DestroyObjects ()
{
while (cxpf>)
{
--cxpf;
(pfx[cxpf])();
}
}
//从这条pragma init_seg语句起,所有全局变量、静态变量不再由编译器、链接器默认处理其初始化与析构,
//而是由程序自身来显示执行其初始化、析构:
#pragma init_seg(".mine$m", myexit)
A bbbb(); //全局变量
A cccc(); //全局变量
int main ()
{
InitializeObjects(); //显示初始化pragma init_seg语句作用下的那些全局变量
DestroyObjects(); //显示销毁pragma init_seg语句作用下的那些全局变量
} 程序运行结果:
A():
A():
A():
~A():
~A():
~A():
#pragma init_seg的更多相关文章
- #pragma详解
在#Pragma是预处理指令它的作用是设定编译器的状态或者是指示编译器完成一些特定的动作.#pragma指令对每个编译器给出了一个方法,在保持与C和C ++语言完全兼容的情况下,给出主机或操作系统专有 ...
- #pragma详细解释(一)
#pragma详细解释 #pragma详细解释(一) 2010-04-18 14:21:00| 分类: 默认分类 | 标签: |字号大中小订阅 在#Pragma是预处理指令它的作用是设定编 ...
- 关于Pragma
/** This is a introduction of how to use pragma. */ #pragma once /// This is used for include the he ...
- #pragma命令详解
每种C和C++的实现支持对其宿主机或操作系统唯一的功能.例如,一些程序需要精确控制超出数据所在的储存空间,或着控制特定函数接受参数的方式.#pragma指示使每个编译程序在保留C和C++语言的整体兼容 ...
- 【转载】C++内存分配
原文:C++内存分配 内存泄露相信对C++程序员来说都不陌生.解决内存泄露的方案多种多样,大部分方案以追踪检测为主,这种方法实现起来容易,使用方便,也比较安全. 首先我们要确定这个模块的主要功能: 能 ...
- VC++全局变量初始化
目录 第1章说明 2 1.1 程序启动 2 1.2 强符号.弱符号 2 1.3 动态初始化顺序 3 1.4 exe调用dll 4 1.5 禁用动态初始化 4 1.6 ...
- [原] blade中C++ singleton的实现
最近看了龚大大KalyGE中的singleton, 觉得非常不错(C++中线程安全并且高效的singleton). 可惜blade的代码都是C++03的, 没有使用C++11的任何特性. 笔者对于si ...
- [百度空间] [转] 在 Visual C++ 中控制全局对象的初始化顺序
from: http://blog.csdn.net/classfactory/archive/2004/08/07/68202.aspx 在 C++ 中,同一个翻译单位(.cpp文件)里的全局对象的 ...
- 使用Visual Leak Detector检测内存泄漏[转]
1.初识Visual Leak Detector 灵活自由是C/C++语言的一大特色,而这也为C/C++程序员出了一个难题.当程序越来越复杂时,内存的管理也会变得越加复杂,稍有不慎就会出现内存问题 ...
随机推荐
- selenium(java)处理HTML5的视频播放
大多数浏览器使用控件(如 Flash) 来播放规频,但是,不同的浏览器需要使用不同的插件.HTML5 定义了一个新的元素<video>,,指定了一个标准的方式来嵌入电影片段.IE9+.Fi ...
- 年中总结大会--总结学习笔记, 技术部IT部门年中总结报告该怎么写
组织年中总结大会该如何组织 1. 通知每一位员工, 总结自己的半年工作(提前多少天, 截至日期) 2. 会场布置 3. 部门领导总结 4. 中场休息, 或节目表演, 合影等 5. 表彰 6. 交流(提 ...
- hibernate+pageBean实现分页dao层功能代码
今天闲来无事,摆弄了一下分页,突然发现很多代码长时间不用就生梳了,虽然有些基础,但没有一篇整合的.这里还是简单示例,主要是以后自己翻着看不用百度找啊找找不到一篇想要的 1.PageBean实体类,一页 ...
- 小知识:匿名类和lambda有什么区别?
我只发现了关于this的区别: 匿名类的this,是指匿名类的实例对象. lambda的this,是指外部类的实例对象. 测试代码如下: /** * Created by LarryZeal on 2 ...
- 使用openstackclient调用Keystone v3 API
本文内容属于个人原创,转载务必注明出处: http://www.cnblogs.com/Security-Darren/p/4138945.html 考虑到Keystone社区逐渐弃用第二版身份AP ...
- HttpClient后台post 请求webapi
1.请求方法 /// <summary> /// httpClient 请求接口 /// </summary> /// <param name="url&quo ...
- Gateway/Worker模型 数据库使用示例
From: http://www.bubuko.com/infodetail-777418.html 1.数据库配置Applications/XXX/Config/Db.php <?php na ...
- linux中service的问题
1.描述问题 2.解决方案 systemctl stop firewalld systemctl mask firewalld Then, install the iptables-services ...
- @Mock与@InjectMocks的区别
@Mock: 创建一个Mock. @InjectMocks: 创建一个实例,简单的说是这个Mock可以调用真实代码的方法,其余用@Mock(或@Spy)注解创建的mock将被注入到用该实例中. 注意: ...
- linux任务计划 chkconfig工具 systemd管理服务 unit介绍 target介绍
linux任务计划 任务计划:特定时间备份数据,重启服务,shell脚本,单独的命令等等. 任务计划配置文件:cat /etc/crontab [root@centos7 ~]# cat /etc/c ...