编译相关
#pragma init_seg
- 概述
- 是微软
C++编译器中的一个编译指令,专门用于精细控制全局静态对象(包括静态变量)的构造和析构顺序 - 在解决复杂的初始化依赖或
DLL加载问题时非常有用
- 是微软
- 语法
|
1 2 3 |
#pragma init_seg( { compiler | lib | user | "section-name" [, func-name] } ) #pragma init_seg("user_defined") |
- 优先级
compiler
最高优先级
此组别的对象最先构造,最后析构
通常保留给C/C++运行时库本身使用(如cin,cout的初始化)lib
中等优先级
此组别的对象在compiler组之后构造,在user组之前析构。适用于第三方类库供应商user
默认及最低优先级
大多数用户代码默认属于此组。此组别的对象最后构造,最先析构section-name
如user_defined,特殊模式
此组别的对象不会自动初始化
它允许你显式指定一个自定义的段(节)名称,编译器只会将所需对象的构造函数指针放置在该命名的段中,而不会自动调用它们
你的代码必须手动遍历该段并调用每个构造函数来完成初始化
- 示例
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
#include <stdio.h> // 1. 定义一个函数指针类型,用于指向构造函数/析构函数 typedef void (__cdecl *PF)(void); // 2. 定义两个标记,用来确定自定义段的起始和结束边界 // 编译器会将位于这两个标记之间的函数指针视为需要手动初始化的对象 #pragma section(".my_initializer$a", read) __declspec(allocate(".my_initializer$a")) const PF InitSegStart = (PF)1; #pragma section(".my_initializer$z", read) __declspec(allocate(".my_initializer$z")) const PF InitSegEnd = (PF)1; // 3. 声明手动初始化函数 void InitializeMyObjects() { const PF *x = &InitSegStart; // 遍历段中的所有函数指针并调用 for (++x; x < &InitSegEnd; ++x) { if (*x) { (*x)(); // 调用构造函数 } } } // 4. 使用自定义段,并声明一个全局对象 #pragma init_seg(".my_initializer$m") // 注意段名格式 class MyCriticalClass { public: MyCriticalClass() { puts("MyCriticalClass constructed manually!"); } ~MyCriticalClass() { puts("MyCriticalClass destroyed!"); } }; MyCriticalClass CriticalObj; // 这个对象的构造将由 InitializeMyObjects 控制 // 5. 在程序合适的地方(如DLL的初始化函数)调用手动初始化 int main() { // ... 其他初始化代码 ... InitializeMyObjects(); // 此时才会真正构造 CriticalObj // ... 程序逻辑 ... return 0; } |
dll相关
关于dll延迟加载
- 隐式加载
- 一个
dll的使用需要依赖头文件和Lib文件,直接以普通的接口的方式来调用dll的函数,这种方式就是隐式加载
加载dll,链接了lib,这种情况lib是作为导入库,区别于加载静态库时lib是作为静态库 dll的隐式加载默认会在mian之前进行dll的加载工作
- 一个
- 显示加载
- 即
dll的加载是通过loadLibrary实现,函数调用通过GetProcAddress来实现
- 即
- 延迟加载-显示加载
- 对于
DLL的显示加载实现延迟加载比较容易,因为LoadLibrary的调用时机可以通过代码来控制
- 对于
- 延迟加载-隐式加载
- 默认的隐式加载的
dll是在mian函数发生之前触发,无法做到延迟加载 - 但
windows提供了一种解决方案,在工程项目中设置延迟加载的dll即可实现延迟加载 - 链接器-输入-延迟加载的
Dll
- 默认的隐式加载的
- 相关注意项
- 延迟加载是 针对隐式链接
DLL的 - 一个导出了字段(如全局变量)的
DLL是无法延迟载入的 - 系统的
DLL一般都是无法延迟载入的,如Kernel32.dll
- 延迟加载是 针对隐式链接
- 需要链接下面的库
|
1 |
delayimp.lib |
dll双形态在不同系统的表现
- 在
win10,win11,运行正常 - 在
win7上面,调试发现,会在BuildRuntime内部这一行奔溃- 解决:
- 调试发现,编出来的
release开着优化时存在此问题,而禁用优化(/Od)后则不存在 - 于是把
release的优化关掉了来处理此问题
|
1 |
GET_ADDR(kernel32_module, GetModuleHandleW); |
vs相关
符合模式
- 编译器选项
/permissive- 核心目标是让编译器以更严格的标准
C++规则来检查你的代码
- 特点
- 严格遵循C++标准,禁用大部分微软特定扩展
- 严格,能识别并报告不符合标准的代码
threadSafeInit
- 开启
/Zc:threadSafeInit- 当代码首次执行到局部静态变量的声明时,编译器会插入额外的线程安全保证代码
- 这通常涉及使用互斥锁或类似原子操作进行双重检查(
Double-Checked Locking),确保即使多个线程同时调用,该变量也只会被初始化一次
- 禁用
/Zc:threadSafeInit-- 编译器会移除以保证线程安全而添加的同步机制
- 意味着,如果多个线程在同一时刻首次访问该局部静态变量,可能会同时执行初始化代码,导致未定义行为
- 常见后果包括重复构造对象、资源泄漏,或者更严重地,一个线程可能使用到另一个线程尚未完成初始化的对象,进而引发程序崩溃
- 配置属性-
c/c++-命令行-附加选项
strictStrings
- 概述
- 一个一致性模式设置,它直接影响编译器对字符串字面量类型转换的严格程度
- 开启
/Zc:strictStrings- 严格遵循
C++标准 - 类型:严格,要求指针有
const限定
- 禁用
/Zc:strictStrings-- 宽松处理,允许非标准转换
- 类型:宽松,允许字符串字面量赋值给非
const指针
- 配置属性-
c/c++-命令行-附加选项
rc相关
vs rc里面覆盖资源的问题
- 资源视图添加了一张
bitmap,png下面显示IDB_PNG_TEST - 在
png下面拷贝了IDB_PNG_TEST,生成了IDB_PNG_TEST1 - 修改了
IDB_PNG_TEST1对应的ID和文件名 - 用同样的流程添加了很多张图片后发现,本地对应的其他图片被修改成第一张的内容
声明:本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 静态库03/15
- ♥ COM组件_303/07
- ♥ About DLL10/02
- ♥ 编译器扩展语法:一07/06
- ♥ Windows动态库(DLL)详细学习:一05/30
- ♥ DLL加载方式10/02