热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

解析在main函数之前调用函数以及对设计的作用详解

本篇文章是对在main函数之前调用函数以及对设计的作用进行了详细的分析介绍,需要的朋友参考下
前几天为新员工写一个简单的测试框架,可让他们方便的写测试用例并且执行。期间遇到一个问题就是如何让他们增加测试用例而用不影响测试框架的代码?c++的单件模式可以解决这个问题,但是其中一个难点是要在main之前注册单件。c++可以通过构造函数来实现注册,c如何注册?
最后查了下资料,原来可以定义在main之前调用的函数!有了这个特性可以改善c的模块化设计。
特性介绍:
如果想定义在main函数之前调用的函数,可以在函数的声明之后加上一句“__attribute__((constructor))”,如下:
int before()__attribute__((constructor));
如果想定义在main函数之后调用的函数,可以在函数的声明之后加上一句“__attribute__((destructor))”,如下:
int after()__attribute__((destructor));
可以看得出来,应该类似于c++中的构造和析构。

一些细节问题:
写测试代码测试了一下这个程序,发现几点:
1、before在main之前调用,调用之前,各个全局变量已经完成初始化。也就是说,这些函数是在全局变量初始化之后,main函数之前调用的。这一点是非常重要的,否则可能会引起很多的问题。
2、after在main之后调用,但是有一点比较特殊,必须是在main中return的话才执行,否则,需要通过atexit执行某函数。这个特性目前对我没有太大的用处。
3、在main函数之前调用的函数可以声明为static。
4、在main函数之前调用的函数可以调用多个。这里就有一个问题,就是这些函数的调用顺序的问题。这个问题首先是一个设计的问题,也就是,我们应该设计这些函数为顺序无关的函数。另外,调用顺序和编译的顺序相关,我在linux下使用make进行编译,发现最后编译的源文件中的函数会最先调用。
5、可以在库(动态库和静态库)中定义这样的函数。

用对设计的作用:
1、可以优化c++中的单件模式。参考《设计模式》
单件模式有一个最大的特点就是可以在运行过程中连接单件。如果使用条件语句来决定使用哪个单件硬性限定了可能的单件集合。所以,书中引入了一个单件注册表的概念,书中对单件注册表的初始化采用的是如下的做法:
首先定义一个单件类,在单件类的构造函数中调用单件的注册函数注册自身:
代码如下:

MySingleton::MySingleton()
{
...
Singleton::Register("MySingleton", this);
}

这个函数是怎么被调用的那?可以定义一个静态实例:
static MySingleton theSingleton;
这样就会在main函数之前调用MySingleton的构造函数来构造这个静态实例,从而达到像注册表注册的目的。
这个方案有个缺点:它能够成功存在一个前提,就是在theSingleton实例化之前,单件注册表列表必须存在,否则会失败。则其实只是一个可能失败的点,如果MySingleton还应用其他的全局变量,则可能这个时候这些全局变量还没有初始化。
解决这个问题的一个方案就是将单件注册的时间由构造函数移到main函数之前调用的函数中来。
定义函数:
代码如下:

static void before_main()
{
Singleton::Register("MySingleton", &theSingleton);
}
声明:
staitc void before_main()__attribute__((constructor));

before_main会在main函数之前调用,而调用时全局变量已经全部初始化,这样就可以避免上面的问题。
其实单件不单单可以在c++(面向对象)中使用,也可以在c中使用。而且有了c的这个特性后,单件更好用。
2、构造插件开发框架,而不用对框架进行更改。
构造插件开发框架的一个问题是:如何新增一个插件而不用修改主框架代码就可以调用插件代码。一般情况下都会使用插件注册机制。也就是框架对外提供注册接口,插件使用这些接口进行注册。c要实行此功能,一个可行的方案是在插件中定义main之前执行的函数,在此函数中调用插件注册接口完成注册。(注:这里讨论的是插件的静态加载)。
3、一个模块有一些初始化工作要做,使用这种机制可以不更改main或者函数。
抛开插件框架,使用这个特性也可以对c的模块化进行很多优化。比如,可以把各个模块的初始化工作放在main之前进行从而防止对main的频繁修改。
注:本文描述的环境为linux c,c++。

推荐阅读
  • 从码农到创业者:我的职业转型之路
    在观察了众多同行的职业发展后,我决定分享自己的故事。本文探讨了为什么大多数程序员难以成为架构师,并阐述了我从一家外企离职后投身创业的心路历程。 ... [详细]
  • Python 工具推荐 | PyHubWeekly 第二十一期:提升命令行体验的五大工具
    本期 PyHubWeekly 为大家精选了 GitHub 上五个优秀的 Python 工具,涵盖金融数据可视化、终端美化、国际化支持、图像增强和远程 Shell 环境配置。欢迎关注并参与项目。 ... [详细]
  • 对于许多初学者而言,遇到总线错误(bus error)或段错误(segmentation fault/core dump)是极其令人困扰的。本文详细探讨了这两种错误的成因、表现形式及解决方法,并提供了实用的调试技巧。 ... [详细]
  • 主调|大侠_重温C++ ... [详细]
  • 本文详细介绍了Java中实现异步调用的多种方式,包括线程创建、Future接口、CompletableFuture类以及Spring框架的@Async注解。通过代码示例和深入解析,帮助读者理解并掌握这些技术。 ... [详细]
  • 本文详细介绍了如何在 Android 中使用值动画(ValueAnimator)来动态调整 ImageView 的高度,并探讨了相关的关键属性和方法,包括图片填充后的高度、原始图片高度、动画变化因子以及布局重置等。 ... [详细]
  • 本文详细介绍了如何解压并安装MySQL集群压缩包,创建用户和组,初始化数据库,配置环境变量,并启动相关服务。此外,还提供了详细的命令行操作步骤和常见问题的解决方案。 ... [详细]
  • CentOS 6.8 上安装 Oracle 10.2.0.1 的常见问题及解决方案
    本文记录了在 CentOS 6.8 系统上安装 Oracle 10.2.0.1 数据库时遇到的问题及解决方法,包括依赖库缺失、操作系统版本不兼容、用户权限不足等问题。 ... [详细]
  • 本文详细介绍了如何在Ubuntu的Enlightenment (E17) 桌面环境中管理和优化桌面图标及根菜单。通过本文,您将了解这些功能的作用及其配置方法。 ... [详细]
  • 本文详细介绍了Linux系统中的进程管理函数,涵盖了获取进程ID、用户ID、创建子进程、信号处理等关键操作。通过这些函数,开发者可以更好地控制和管理进程行为。 ... [详细]
  • 本文详细介绍了如何在Linux系统中创建和管理DB2数据库,包括用户切换、数据库创建、错误处理、连接与断开、表空间和缓冲池的创建,以及用户权限管理和数据导入导出等操作。 ... [详细]
  • 本文深入探讨了UNIX/Linux系统中的进程间通信(IPC)机制,包括消息传递、同步和共享内存等。详细介绍了管道(Pipe)、有名管道(FIFO)、Posix和System V消息队列、互斥锁与条件变量、读写锁、信号量以及共享内存的使用方法和应用场景。 ... [详细]
  • CentOS 7.6环境下Prometheus与Grafana的集成部署指南
    本文旨在提供一套详细的步骤,指导读者如何在CentOS 7.6操作系统上成功安装和配置Prometheus 2.17.1及Grafana 6.7.2-1,实现高效的数据监控与可视化。 ... [详细]
  • 本文详细介绍了 Linux 系统中用户、组和文件权限的设置方法,包括基本权限(读、写、执行)、特殊权限(SUID、SGID、Sticky Bit)以及相关配置文件的使用。 ... [详细]
  • Shell脚本中变量操作详解
    本文基于《鸟哥的Linux私房菜》一书,详细介绍了Shell脚本中变量的使用方法,包括变量的赋值规则、字符串处理技巧以及环境变量的管理等,旨在帮助读者更好地理解和使用Shell中的变量。 ... [详细]
author-avatar
Saber木木-汐7rv
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有