利用#pragma init_seg 控制构造顺序

本文探讨了C++标准中全局对象的构造顺序,特别关注不同编译单元间全局对象的初始化顺序。通过使用#pragma init_seg预处理器指令,可以在文件范围内控制静态对象的构造和析构顺序,有效解决潜在的dead-reference问题。具体实例展示了如何在不同链接顺序下应用此指令,并通过代码验证了其对构造顺序的影响。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前言

C++标准中,处于同一编译单元(cpp)的全局对象按其声明次序初始化并倒序析构,但标准中没有定义处于不同编译单元的全局对象的初始化顺序。假如有个Log对象负责程序日志的记录,如果程序结束时,有某个全局对象出现类似于资源释放失败的错误,该对象会调用Log记录错误,这时,Log可能已经被销毁了,这就是所谓的dead-reference问题。

使用VC中的#pragma init_seg预处理指令可以解决这个问题。

#pragma init_seg(compiler)

#pragma init_seg(lib)

#pragma init_seg(user)

#pragma init_seg("user_defined_segment_name")

前三个指令,初始化优先次序依次降低,但都先于普通的全局变量构造,后于它们析构。

例如cout就是使用compiler级别构造的:

#pragma init_seg(compiler)

_CRTIMP2 ostream cout(&fout);

来自微软的详细描述:

http://support.microsoft.com/kb/104248

KB

Microsoft C++ 编译器可以通过使用 #pragama init_seg 预处理器指令在文件范围内控制您的静态对象的构造和析构。

有四个选项为 init_seg 预处理器指令:

     #pragma init_seg(compiler)
     #pragma init_seg(lib)
     #pragma init_seg(user)
     #pragma init_seg("user_defined_segment_name")
注意: 一个源文件仅一个 init_seg 指令可以出现。 否则,编译器会产生 "error C2356: initialization segment must not change during translation unit."   

此指令旨在使开发人员能够对构造函数分组,对于有依赖关系的对象这将很有用。使用 #pragma init_seg(compiler) 的对象在所有其他对象之前构造并在其它对象析构之后最后析构。这个指令一般用于运行时库中的对象。比如, cin 和 cout就在此分组里,如果你的对象也分在compiler组里面,在构造或析构里面使用cout时可能会遇到问题。

使用 #pragma init_seg(lib) 的对象构造于使用 #pragma init_seg(compiler)的对象之后,但是前于其它对象。使用 #pragma init_seg(user) 的对象构造于使用 #pragma init_seg(compiler) 和 #pragma init_seg(lib) 的对象之后。换句话说,本组与其它未分组的静态对象同时构造。

一种可以控制构造顺序的方法是更改链接顺序,同一init_seg分组里链接越早的模块构造越晚。

务必注意 C++ 语言不保证非派生对象的构造顺序 , C++ 语言保证基类早于派生类的对象构造。

#pragma init_seg("user_defined_segment_name") 预处理器指令将构造函数的地址放入到逻辑段"user_defined_segment_name"。 此选项仅仅在你需要修改启动代码的需求时,调用这些构造函数。 

实例

下面的代码示例(四个源文件)演示上面的说法。 编译所有的源文件之后, 两种方式如下链接,运行它们。 从输出查看哪些init_seg 选项依赖链接顺序。 

使用 Visual C++ 32 位 Edition 版本:
link file1 file2 file3 file4 /out:demo1.exe
link file4 file3 file2 file1 /out:demo2.exe
With Visual C++ 16-位版本,使用:
link file1 file2 file3 file4, demo1;
link file4 file3 file2 file1, demo2

示例代码

// file1.cpp
// command line: cl /c file1.cpp
#pragma init_seg(compiler)
#include<stdio.h>
class MyCompClass
{
public:
MyCompClass(){ printf("In the ctor of MyCompClass\n");}
~MyCompClass(){ printf("In the dtor of MyCompClass\n");}
} MyComp;
// file2.cpp
// command line: cl /c file2.cpp
#pragma init_seg(lib)
#include<iostream.h>
class MyLibClass
{
public:
MyLibClass(){cout<<"In the ctor of MyLibClass"<<endl;}
~MyLibClass(){cout<<"In the dtor of MyLibClass"<<endl;}
} MyLib;
// file3.cpp
// command line: cl /c file3.cpp
#pragma init_seg(user)
#include<iostream.h>
class MyUserClass
{
public:
MyUserClass(){cout<<"In the ctor of MyUserClass"<<endl;}
~MyUserClass(){cout<<"In the dtor of MyUserClass"<<endl;}
} MyUser;
// file4.cpp
// command line: cl /c file4.cpp
#include<iostream.h>
class MyRegularClass
{
public:
MyRegularClass(){cout<<"In the ctor of MyRegularClass"<<endl;}
~MyRegularClass(){cout<<"In the dtor of MyRegularClass"<<endl;}
} MyRegular;
void main(){}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值