博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
通过一个示例看全局对象的构造和析构以及atexit的使用
阅读量:4030 次
发布时间:2019-05-24

本文共 5006 字,大约阅读时间需要 16 分钟。

示例执行

示例输出

/Users/luogw/CLionProjects/untitled2/cmake-build-debug/untitled2construct, call ResManger() // 库的全局变量construct, call GlobalClass()2construct, call GlobalClass()call main()Hello, World! // 库的hello函数的输出construct, call CommonClass()register atexitnext line call exit(0)atexit callback, lambda expressionatexit callback, beforeExitF()destruct, call ~GlobalClass()destruct, call ~GlobalClass()2destruct, call ~ResManger()Process finished with exit code 0

对应的可执行程序的源代码如下

#include 
#include
#include "lib/include/library.h"using namespace std;class GlobalClass {public: GlobalClass() { cout << "construct, call GlobalClass()" << endl; } ~GlobalClass() { cout << "destruct, call ~GlobalClass()" << endl; }};class GlobalClass2 {public: GlobalClass2() { cout << "construct, call GlobalClass()2" << endl; } ~GlobalClass2() { cout << "destruct, call ~GlobalClass()2" << endl; }};class CommonClass {public: CommonClass() { cout << "construct, call CommonClass()" << endl; } ~CommonClass() { cout << "destruct, call ~CommonClass()" << endl; }};//全局对象GlobalClass2 G_Class2;GlobalClass G_Class;void beforeExitF() { cout << "atexit callback, beforeExitF()" << endl;}int main() { cout << "call main()" << endl; // 调用动态库的方法 hello(); CommonClass commonClass; cout << "register atexit" << endl; //注册应用退出时的回调方法 atexit(beforeExitF); //注删应用退出前的回调lamdba表达式 atexit([] { cout << "atexit callback, lambda expression" << endl; }); //ateixt是FILO的原侧,先进后出,即后注册的先执行 cout << "next line call exit(0)" << endl; /* * 这时调exit提前结束了进程,所以CommonClass的析构方法不会调到,因为main没有执行return,即main没有出栈 * exit跟return的区别,具体看这里的讨认 https://www.zhihu.com/question/26591968 */ exit(0);// return 0;}

知识点

  • 一个可执行程序的全局变量在main方法执行之前构造/初始化,在应用退出前析构。全局变量包括执行程序和其依赖的库定义的全局变量,库包括静态库和动态库。
  • 全局变量的构造与析构是配对的,但先构造的会后析构。库定义的全局变量比可执行的全局变量要先构造。
  • 全局变量初始化的顺序跟其在源文件中定义的位置一致,定义在前面先得到构造
  • atexit的可执行代码段的注册与执行是FILO的规则,即后注册的先执行。这也就办什么全局变量先构造的后析造的原因。全局变量的析构是利用atexit实现(编译器帮我们做的)

代码说明

  • 示例的运行环境是macOS,IDE 是Clion,代码构建是Clion默认的Cmake
  • 示例为一个可执行程序工程跟一个动态库工程,动态库构建后产物(动态库文件还有头文件)拷贝到可执行程序工程根目录下的lib目录

动态库项目

在这里插入图片描述

library.h

#ifndef UNTITLED1_LIBRARY_H#define UNTITLED1_LIBRARY_Hvoid hello();#endif //UNTITLED1_LIBRARY_H

library.cpp

#include "library.h"#include 
using namespace std;class ResManger {public: ResManger() { cout << "construct, call ResManger()" << endl; } ~ResManger() { cout << "destruct, call ~ResManger()" << endl; }};ResManger resManger;void hello() { std::cout << "Hello, World!" << std::endl;}

CMakeLists.txt

cmake_minimum_required(VERSION 3.17)project(untitled1)set(CMAKE_CXX_STANDARD 14)add_library(untitled1 SHARED library.cpp library.h)

其它的默认配置,可以用otool来看一下动态库使用时的路径配置情况

$ otool -L cmake-build-debug/libuntitled1.dylib            cmake-build-debug/libuntitled1.dylib:        @rpath/libuntitled1.dylib (compatibility version 0.0.0, current version 0.0.0)        /usr/lib/libc++.1.dylib (compatibility version 1.0.0, current version 904.4.0)        /usr/lib/libSystem.B.dylib (compatibility version 1.0.0, current version 1292.60.1)

可执行程序工程

在这里插入图片描述

main.cpp

#include 
#include
#include "lib/include/library.h"using namespace std;class GlobalClass {public: GlobalClass() { cout << "construct, call GlobalClass()" << endl; } ~GlobalClass() { cout << "destruct, call ~GlobalClass()" << endl; }};class GlobalClass2 {public: GlobalClass2() { cout << "construct, call GlobalClass()2" << endl; } ~GlobalClass2() { cout << "destruct, call ~GlobalClass()2" << endl; }};class CommonClass {public: CommonClass() { cout << "construct, call CommonClass()" << endl; } ~CommonClass() { cout << "destruct, call ~CommonClass()" << endl; }};//全局对象GlobalClass2 G_Class2;GlobalClass G_Class;void beforeExitF() { cout << "atexit callback, beforeExitF()" << endl;}int main() { cout << "call main()" << endl; // 调用动态库的方法 hello(); CommonClass commonClass; cout << "register atexit" << endl; //注册应用退出时的回调方法 atexit(beforeExitF); //注删应用退出前的回调lamdba表达式 atexit([] { cout << "atexit callback, lambda expression" << endl; }); //ateixt是FILO的原侧,先进后出,即后注册的先执行 cout << "next line call exit(0)" << endl; /* * 这时调exit提前结束了进程,所以CommonClass的析构方法不会调到,因为main没有执行return,即main没有出栈 * exit跟return的区别,具体看这里的讨认 https://www.zhihu.com/question/26591968 */ exit(0);// return 0;}

CMakeLists.txt

cmake_minimum_required(VERSION 3.17)project(untitled2)set(CMAKE_CXX_STANDARD 14)//添加动态库链接的路径link_libraries("${CMAKE_CURRENT_SOURCE_DIR}/lib/libuntitled1.dylib")add_executable(untitled2 main.cpp)

用otool查看可执行程序依赖的动态库的路径如下

注:默认链接了绝对路径

# luogw @ ericluodeMacBook-Pro in ~/CLionProjects/untitled2 [17:13:12] $ otool -l cmake-build-debug/untitled2 | grep path         name @rpath/libuntitled1.dylib (offset 24)         path /Users/luogw/CLionProjects/untitled2/lib (offset 12)

参考资料

转载地址:http://pumbi.baihongyu.com/

你可能感兴趣的文章
Spring JTA应用之JOTM配置
查看>>
spring JdbcTemplate 的若干问题
查看>>
Servlet和JSP的线程安全问题
查看>>
GBK编码下jQuery Ajax中文乱码终极暴力解决方案
查看>>
jQuery性能优化指南
查看>>
Oracle 物化视图
查看>>
Multi-Task Networks With Universe, Group, and Task Feature Learning-阅读笔记
查看>>
A Survey of Zero-Shot Learning: Settings, Methods, and Applications-阅读笔记
查看>>
Cross-Domain Review Helpfulness Prediction -论文阅读
查看>>
NLP数据增强方法-(一)
查看>>
BERT+实体-百度ERNIE优化了啥
查看>>
NLP数据增强方法-动手实践
查看>>
学习让机器学会学习-Meta Learning课程笔记-1
查看>>
学习让机器学会学习-Meta Learning课程笔记-2
查看>>
RNN及其变种LSTM/GRU/SRU
查看>>
我还不知道Attention有哪些-公式代码都带你搞定
查看>>
自学习-怎么让对话助手越来越强
查看>>
BERT-flow:bert的向量表达是SOTA吗?
查看>>
Preprocessing data-sklearn数据预处理
查看>>
Java实现Oracle到MySQL的表迁移
查看>>