C++如何在main函数开始之前(或结束之后)执行一段逻辑?

  • 1. 问题
  • 2. 考察的要点
  • 3. 解决策略
    • 3.1. 方案一:使用GCC的拓展功能
    • 3.2. 方案二:使用全局变量
    • 3.3. 方案三:atexit
  • 4. Demo测试
    • 4.1. 测试代码
    • 4.2. 执行结果
  • 5. 程序异常退出场景
    • 5.1. 存在的问题
    • 5.2. 解决方案
      • 5.2.1. 原理
      • 5.2.2. 示例代码
      • 5.2.3. 执行结果
      • 5.2.4. 特殊说明
  • 6. 参考文档

1. 问题

我们知道C/C++程序的执行逻辑是从main函数开始,到main函数结束。但是,有时我们需要在main函数开始之前或结束之后执行一段逻辑,比如:

  1. 如何在main函数开始之前执行一段逻辑?
  2. 如何在main函数结束之后执行一段逻辑?

有办法实现吗?在往下阅读之前,请先思考一下。

2. 考察的要点

C++程序的代码执行逻辑。 全局变量|静态变量的理解。

3. 解决策略

3.1. 方案一:使用GCC的拓展功能

GCC编译器的拓展功能,通过 __attribute__ 关键字注册“在main函数开始之前或结束之后”执行的回调函数。

__attribute((constructor)) void before_main() {std::cout << "before main" << std::endl;
}__attribute((destructor)) void after_main() {std::cout << "after main" << std::endl;
}

3.2. 方案二:使用全局变量

全局变量会在进程刚启动的时候就初始化,在进程结束的时候被销毁。所以:全局对象的初始化会在main函数执行之前被执行;全局对象的销毁会在main函数执行之后被执行。

结合C++类的构造函数和虚构函数的特点,可以专门定义一个类来处理main函数开始之前和结束之后的逻辑(为了保证这个类只有一个全局对象,建议将这个类设计成单例模式),然后在main之前声明这个类的一个全局变量。

class BeforeAndAfterMain
{
public:static BeforeAndAfterMain& GetInstance(){static BeforeAndAfterMain instance;return instance;}~BeforeAndAfterMain(){std::cout << "Global object destory after main" << std::endl;}private:BeforeAndAfterMain(){std::cout << "Global object construct before main" << std::endl; }BeforeAndAfterMain(const BeforeAndAfterMain&) = delete;BeforeAndAfterMain& operator=(const BeforeAndAfterMain&) = delete;
};auto& g_before_and_after_main = BeforeAndAfterMain::GetInstance();

3.3. 方案三:atexit

针对main函数结束之后的逻辑,可以使用atexit函数注册一个回调函数,在main函数执行之后被执行。

#include <cstdlib>void at_main_exit(){std::cout << "at_main_exit" << std::endl;
}

4. Demo测试

4.1. 测试代码

完整测试代码如下:

#include <iostream>
#include <cstdlib>__attribute((constructor)) void before_main() {std::cout << "before main" << std::endl;
}__attribute((destructor)) void after_main() {std::cout << "after main" << std::endl;
}class BeforeAndAfterMain
{
public:static BeforeAndAfterMain& GetInstance(){static BeforeAndAfterMain instance;return instance;}~BeforeAndAfterMain(){std::cout << "Global object destory after main" << std::endl;}private:BeforeAndAfterMain(){std::cout << "Global object construct before main" << std::endl; }BeforeAndAfterMain(const BeforeAndAfterMain&) = delete;BeforeAndAfterMain& operator=(const BeforeAndAfterMain&) = delete;
};auto& g_before_and_after_main = BeforeAndAfterMain::GetInstance();void at_main_exit(){std::cout << "at_main_exit" << std::endl;
}int main() {// https://en.cppreference.com/w/cpp/header/cstdlibatexit(at_main_exit);std::cout << "main begin" << std::endl;int a = 10;int b = 5;// crash to exit// int b = 0;int c = a / b;std::cout << "a /b = " << c << std::endl;std::cout << "main end" << std::endl;return 0;
}

4.2. 执行结果

before main
Global object construct before main
main begin
a /b = 2
main end
at_main_exit
Global object destory after main
after main

5. 程序异常退出场景

5.1. 存在的问题

上面的Demo,把

    int b = 5;

替换成

    // crash to exitint b = 0;

会导致程序异常(除数不能为0)退出,输出如下:

before main
Global object construct before main
main begin
Floating point exception

三种main函数结束后的逻辑均未被执行。说明:程序异常退出时(如:crash),“main函数结束后的逻辑均”不被执行,不能cover住这种场景。

5.2. 解决方案

5.2.1. 原理

当程序崩溃时,操作系统会发送一个信号给程序,通知它发生了异常。在 C++中,可以通过 signal 函数来注册一个信号处理程序,使程序能够在接收到该信号时执行自定义的代码。

程序的执行流程:

  1. 执行程序,按正常逻辑执行。
  2. 程序崩溃,异常退出,根据不同的崩溃原因,操作系统能识别出不同的崩溃信号(signal)。
  3. 操作系统发送对应的崩溃信号(signal)给执行程序。
  4. 执行程序根据提前已注册好的信号处理函数,执行对应的信号处理逻辑。
  5. 信号处理函数执行完毕,通过exit函数退出程序。

这样保证了:虽然程序的主流程崩溃了,但是程序还是能正常结束。这样即使程序崩溃了,还是能够自己完成如:“资源释放”、“状态保存或重置”等一些重要的逻辑。

5.2.2. 示例代码
void signal_handler(int sig) {// 这里编写你的异常信号处理逻辑,比如打印日志,保存状态,捕获堆栈信息等。std::cerr << "signal_handler" << std::endl;// 注意:信号处理程序执行完成,一定要调用exit退出,否则信号处理函数可能会被循环执行。exit(1);
}int main() {// 注册信号处理函数// signal(SIGSEGV, signal_handler);signal(SIGFPE, signal_handler);// https://en.cppreference.com/w/cpp/header/cstdlibatexit(at_main_exit);std::cout << "main begin" << std::endl;int a = 10;// int b = 5;// crash to exitint b = 0;int c = a / b;std::cout << "a /b = " << c << std::endl;std::cout << "main end" << std::endl;return 0;
}
5.2.3. 执行结果
before main
Global object construct before main
main begin
signal_handler
at_main_exit
Global object destory after main
after main
5.2.4. 特殊说明
  1. 当程序崩溃时,可能已经无法正常执行代码,因此需要谨慎地编写信号处理程序,以避免进一步的崩溃或数据损坏。
  2. 信号处理程序执行完成,一定要调用exit退出,否则信号处理函数可能会被循环执行。
  3. 考虑各种可能出现的异常信号,比如:SIGSEGV、SIGFPE、SIGILL、SIGABRT等。这些可能出现的异常,都需要注册对应的信号处理程序。以免出现异常漏捕获的情况。

6. 参考文档

https://blog.csdn.net/zhizhengguan/article/details/122623008 https://blog.csdn.net/MldXTieJiang/article/details/129620160


【SunLogging】

SunLogging

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.xdnf.cn/news/1486361.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

定义限流和降级后的处理⽅法(Sentinel)

SentinelResource的使用 在定义了资源点之后&#xff0c;我们可以通过 Dashboard 来设置限流和降级策略来对资源点进⾏保护。同时还能通过 SentinelResource 来指定出现异常时的处理策略。 SentinelResource ⽤于定义资源&#xff0c;并提供可选的异常处 理和 fallback 配置项…

Qt5--设置QCustomPlot

在你程序中使用QCustomPlot非常简单&#xff1a; 从下载部分获取QCustomPlot的最新版本。像任何其它常规的类文件一样使用qcustomplot.h和qcustomplot.cpp。 对于QtCreator用户 在左侧边栏中你项目的根项目上右击并且选择Add Existing Files... 在出现的文件对话框中&#xf…

成都云飞浩容文化传媒有限公司是真的吗?

在当今这个数字化浪潮汹涌的时代&#xff0c;电商行业无疑是众多行业中的一颗璀璨明星。作为这个行业的佼佼者&#xff0c;成都云飞浩容文化传媒有限公司凭借其专业的电商服务&#xff0c;已经帮助无数企业和个人在电商领域取得了令人瞩目的成绩。 成都云飞浩容文化传媒有限公…

Android 性能之刷新率设置和管理

目录 1. 刷新率和帧率 2. 多种刷新率 3. 基本原理 3.1 屏幕 & 显示控制器 3.2 Composer Service 4. Framework 策略 4.1基本架构 4.2 刷新率设置项的定义 4.2.1 最低刷新率 4.2.2 默认刷新率 & 默认的用户设置刷新率 4.2.2.1 设置入口 4.2.2.2 设置场景 4…

SmartX 超融合 vs vSAN 8:数据库场景下的性能对比

此前&#xff0c;我们以 vSAN 7 为例&#xff0c;对比了 SmartX 与 VMware 超融合在快照、缓存、I/O 路径上的技术差异&#xff0c;及其带来的性能影响。不同于 vSAN 7&#xff0c;vSAN 8 引入了快速存储架构 ESA&#xff08;Express Storage Architecture&#xff09;&#xf…

企业私有云的部署都有哪些方式?

如今常见的企业私有云的部署方式有自建私有云、托管私有云、虚拟私有云、混合云、容器化私有云、本地数据中心部署等。如今&#xff0c;企业私有云的部署呈多样化趋势&#xff0c;以用来满足各个企业的具体需求。以下是RAK部落小编为大家汇总的企业私有云常见的部署方式&#x…

Visual Studio调试Web项目

一、编译运行调试&#xff08;VS快捷键&#xff1a;CtrlF5&#xff09; 缺点&#xff1a;编译运行项目太慢&#xff0c;整体程序有些编译报错运行不了 二、附加到进程调试&#xff08;VS快捷键&#xff1a;CtrlAltP&#xff0c;选择w3wp.exe&#xff09; 无需编译&#xff0c;速…

如何在WeMapEngine中加载显示地图

我们在《全新跨平台GIS引擎将问世》一文中&#xff0c;为你分享了我们自主研发了一款全新跨平台的国产化GIS引擎——WeMapEngine。 现在再为你分享一下&#xff0c;如何在WeMapEngine中加载地图服务的方法&#xff0c;不过该产品暂未发布&#xff0c;因此暂时还无法立即体验。…

HormonyOs之 路由简单跳转

Navigation路由相关的操作都是基于页面栈NavPathStack提供的方法进行&#xff0c;每个Navigation都需要创建并传入一个NavPathStack对象&#xff0c;用于管理页面。主要涉及页面跳转、页面返回、页面替换、页面删除、参数获取、路由拦截等功能。 Entry Component struct Index …

Spring -- 三层架构

T04BF &#x1f44b;专栏: 算法|JAVA|MySQL|C语言 &#x1faf5; 今天你敲代码了吗 应用分层 介绍 在阿里的开发手册里,关于工程结构部分,定义的常见工程的应用分层结构 那么什么是应用分层呢? 应用分层是一种软件开发设计思想,他将应用程序分层N个层次.这N个层次分别负责各…

vue3【详解】内置组件 Transition

用于给元素或组件进入和离开页面添加过渡动画 使用场景 v-if 切换 <button click"show !show">Toggle</button> <Transition><p v-if"show">hello</p> </Transition>v-show 切换 动态组件切换 <Transition name&…

Adobe国际认证详解-ae视频剪辑

AE视频剪辑&#xff0c;即After Effects视频剪辑&#xff0c;是数字媒体时代的一项重要技能。AE不仅提供了丰富的特效和动画功能&#xff0c;还让视频剪辑变得更加高效和有趣。对于想要在职场上脱颖而出、拓宽就业机遇的人来说&#xff0c;掌握AE视频剪辑技能无疑是一个明智的选…

小程序基础总结

组件标签 html wxml div span img a view text image navigator css wxss rpx 小程序响应式单位,在不同设备上自动进行换算 js app.js 项目入口文件 ,通过App()函数启动整个小程序 页面的 .js ,页面入口文件,通过Page()函数创建运行页面 通信模型 渲染层 逻辑层 第三方…

Q*s‘ AGI

最近这段时间&#xff0c;不论是产业界还是学术界&#xff0c;均发表了不少围绕「Q*」的探索和实验&#xff0c;同时随着OpenAI的「草莓QQ糖(皮一下)」计划的曝光与训练的进行&#xff0c;似乎又为当前围绕Q*、AGI、AI4S等领域带来了不少业界争鸣和讨论&#xff0c;而且周围更多…

Electron案例解析——切换主题颜色的案例

效果图 核心 Electron的 nativeTheme.themeSource属性&#xff0c;值是string。有三个参数&#xff1a;system, light 和 dark&#xff0c;用来覆盖、重写Chromium内部的相应的值 Election的api描述值nativeTheme.themeSource被用来覆盖、重写Chromium内部的相应的值system, …

谷粒商城实战笔记-45-商品服务-API-三级分类-查询-递归树形结构数据获取

文章目录 一&#xff0c;准备工作1&#xff0c;启动虚拟机2&#xff0c;启动mysql3&#xff0c;执行MySQL脚本插入分类数据4&#xff0c;关于三级分类 二&#xff0c;Controller层新增接口三&#xff0c;Service层新增接口1&#xff0c;代码实现2&#xff0c;测试 从这一节开始…

实验二 FPGA使用Verilog HDL设计分频器

实验目的 掌握使用Vivado软件进行设计、综合、仿真、布线的方法。掌握FPGA程序的下载方法。掌握使用Verilog HDL设计分频器的方法。 实验要求 采用Verilog HDL语言设计分频器&#xff0c;实现以下功能&#xff1a; 输出时钟周期为1s的分频时钟信号&#xff1b;通过LED灯或数码…

15个在线地图瓦片URL分享

数据是GIS的血液&#xff0c;我们在《10个在线地图瓦片URL分享》一文中为你分享了10个地图瓦片URL链接&#xff0c;现在再为你分享5个在线的瓦片地图URL图源&#xff01; 该在线地图瓦片URL支持在GIS软件或开发平台进行调用&#xff0c;比如ArcGIS、QGIS、Global Mapper、Open…

PowerBI宝藏级资料分享推荐

有同学留言想学习PowerBI&#xff0c;求推荐可以免费学习的网站&#xff0c;最好是比较系统的&#xff0c;不要那种广告或诱导付费才能学习的网站。 这样的学习网站确实有&#xff0c;主打大家要求的免费。 推荐大家几个我一直私藏的宝藏级&#xff0c;学习和使用资料网站. …

《经典图论算法》贝尔曼-福特算法(Bellman-Ford)

摘要&#xff1a; 1&#xff0c;Bellman-Ford 算法的介绍 2&#xff0c;Bellman-Ford 算法为什么可以解决有负权边的图 3&#xff0c;Bellman-Ford 算法为什么不能解决有负权回路的图 4&#xff0c;Bellman-Ford 算法的代码实现和负权回路的判断 5&#xff0c;Bellman-Ford 算法…