Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >深入浅出 C++:与程序终止相关的函数 PART 2 - quick_exit()、_Exit()

深入浅出 C++:与程序终止相关的函数 PART 2 - quick_exit()、_Exit()

作者头像
用户7886150
修改于 2021-02-14 02:09:40
修改于 2021-02-14 02:09:40
1.4K0
举报
文章被收录于专栏:bit哲学院bit哲学院

参考链接: C++ at_quick_exit()

quick_exit() 与 at_quick_exit() (C++11新增) 

[[noreturn]] void quick_exit(int status) noexcept; 

quick_exit() 为 C++11 引入的函数,如果程序有特殊理由,想直接结束、但又不希望呼叫到对象的 destructor 时,就能派上用处。 

相对于 exit() 与 atexit(),quick_exit() 亦有 at_quick_exit(),用来注册当 quick_exit() 调用后,还需处理的事情。C++ 标准同样保证 at_quick_exit() 至少能注册 32 个函数,且执行的顺序与注册的顺序相反。 

extern "C" int at_quick_exit (void (*func)(void)) noexcept;

extern "C++" int at_quick_exit (void (*func)(void)) noexcept; 

at_quick_exit 注册的函数,与 at_exit 注册的是隔离的,两不相干,端看程序是以 exit() 结束、还是 quick_exit() 结束。若 main() 函数正常结束,则是调用 exit()、并触发 at_exit() 注册的函数执行。 

#include <iostream>

#include <cstdlib>

#include <thread>

void Print(const std::string&s, int m)

{

  std::cout << s << ", m_ = " << m

            << ", thread_id = " << std::this_thread::get_id() << std::endl;

}

class MyClass

{

public:

  MyClass(int a) : m_(a) { Print("Constructor", m_); }

  ~MyClass()             { Print("Destructor", m_);  }

  void Show()            { Print("Show", m_);        }

private:

  int m_;

};

void ExitFunction1() { std::cout << "ExitFunction1()" << std::endl; }

void ExitFunction2() { std::cout << "ExitFunction2()" << std::endl; }

void ExitFunction3() { std::cout << "ExitFunction3()" << std::endl; }

void ExitFunction4() { std::cout << "ExitFunction4()" << std::endl; }

MyClass c1(1);

thread_local MyClass c2(2);

int main()

{

  std::cout << "Begin of main()" << std::endl;

  static MyClass c3(3);

  MyClass c4(4);

  c2.Show();

  std::atexit(ExitFunction1);

  std::at_quick_exit(ExitFunction2);

  std::at_quick_exit(ExitFunction3);

  std::at_quick_exit(ExitFunction4);

  std::cout << "Call quick_exit()" << std::endl;

  std::quick_exit(EXIT_SUCCESS);

  std::cout << "End of main()" << std::endl;

sora@sora-VirtualBox:~/cpp/c2$ clang++ -std=c++17 -stdlib=libc++ --pedantic-errors -pthread -o quick_exit quick_exit.cpp

sora@sora-VirtualBox:~/cpp/c2$ ./quick_exit 

Constructor, m_ = 1, thread_id = 140176298325824

Begin of main()

Constructor, m_ = 3, thread_id = 140176298325824

Constructor, m_ = 4, thread_id = 140176298325824

Constructor, m_ = 2, thread_id = 140176298325824

Show, m_ = 2, thread_id = 140176298325824

Call quick_exit()

ExitFunction4()

ExitFunction3()

ExitFunction2()

_Exit() (C++11 新增) 

[[noreturn]] void _Exit (int status) noexcept; 

如果不想在程序结束时,调用任何对象的 destructor、也不想执行任何由 atexit()、at_quick_exit() 注册的函数,则可使用 _Exit() 结束。 

exit() 结束 process 的过程中,除了调用 atexit() 注册的函数,还会 flush 并 close stdio stream。考虑一种情况,如果一个 parent process fork 出一个 child process,若 child process 使用 exit() 结束,child process 会强制 flush 残留在内存中的 stdio stream,而当 parent process 结束时,他也 flush 了 stdio stream,就会导致 flush 两次重复的结果。下面范例说明这种情况: 

#include <iostream>

#include <cstdlib>

#include <unistd.h>

#include <sys/types.h>

#include <sys/wait.h>

int main()

{

  std::cout << "Hello World";

  pid_t pid = fork();

  if (pid == 0) // child process

  {

    std::exit(0);

  }

  else if (pid > 0)

  {

    int status;

    waitpid(pid, &status, 0);

    std::cout << std::endl << "Child process has exited" << std::endl;

  }

  else

  {

    std::cerr << "fork failed" << std::endl;

  }

从下面执行的结果,可看到 Hello World 输出了两次,原因就在于 child process 执行的 exit() 触发了 fluch stdio stream。 

sora@sora-VirtualBox:~/cpp/c2$ clang++ -std=c++17 -stdlib=libc++ --pedantic-errors -pthread -o _Exit _Exit.cpp

sora@sora-VirtualBox:~/cpp/c2$ ./_Exit

Hello WorldHello World

Child process has exited

上述问题,只要将第 10 行改为调用 std::_Exit(0),就不会发生。 

fork() 的目的,是让 child process 与 parent 具有相同的内存内容、再执行不同的代码,所以 fork 前未摧毁的类、未 flush 的 stdio stream 等,都会带到 child process。这些类的 destructor,可能做了某些复杂的行为、不适合重复执行,而 stdio stream 也不适合 flush 重复的内容,在此情况下,就可考虑用 _Exit() 解决问题。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系转载,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
C++一分钟之-构造函数与析构函数
在C++编程领域,构造函数与析构函数是类设计中不可或缺的组成部分,它们分别负责对象的初始化与资源的清理工作。本文将简明扼要地介绍这两者的概念、作用、常见问题、易错点以及如何避免这些问题,配以实用的代码示例,帮助你更好地掌握这一核心知识点。
Jimaks
2024/06/20
1960
C++ delete的三种面貌
为了避免内存泄漏,每个动态内存分配必须有与一个相反的解除分配(Deallocation)操作对应,所以C++中有new操作,那么就存在相反的delete操作,new与delete的关系,就像C语言中malloc()与free()的关系,分别负责内存的申请与释放,只不过C++中的new与delete赋予了其它的功能。当我们使用delete运算符来释放一个由new创建的对象时,我们应该清楚delete完成的工作有: (1)调用对象析构函数; (2)调用operater delete()函数释放内存空间。
恋喵大鲤鱼
2019/02/22
1.4K0
C++ pimpl惯用法
Pimpl(Pointer to Implementation)是C++中的一种设计模式,也是一种惯用法,用于实现封装和隐藏类的实现细节。Pimpl的主要思想是将类的具体实现细节放在一个单独的类中,然后在主类中使用指向该实现类的指针。这有助于减小头文件的依赖性,提高编译速度,同时可以隐藏实现细节,减少对用户的影响。
Linux兵工厂
2024/02/17
3220
C++ pimpl惯用法
C++内存管理深度总结(近万字详解!)
在C语言中,动态内存管理主要通过malloc、calloc、realloc和free这四个函数进行。以下是一个简化的代码示例,展示了如何在C语言中使用这些函数来动态分配、使用和释放内存:
suye
2024/10/16
2460
C++继承、虚函数、RTTI、友元类、异常处理
前面讲到c++的继承是子类在继承时声明继承的权限,之前描述有点不够准确。以下时书中提及的能继承的成员。
歪歪梯
2020/08/17
8320
【重学C++】02 脱离指针陷阱:深入浅出 C++ 智能指针
在上一讲《01 C++如何进行内存资源管理》中,提到了对于堆上的内存资源,需要我们手动分配和释放。管理这些资源是个技术活,一不小心,就会导致内存泄漏。
会玩code
2023/07/08
4890
【重学C++】02 脱离指针陷阱:深入浅出 C++ 智能指针
深入浅出C++虚函数的vptr与vtable
为了实现虚函数,C ++使用一种称为虚拟表的特殊形式的后期绑定。该虚拟表是用于解决在动态/后期绑定方式的函数调用函数的查找表。虚拟表有时会使用其他名称,例如“vtable”,“虚函数表”,“虚方法表”或“调度表”。
公众号guangcity
2019/09/20
4.5K0
深入浅出C++虚函数的vptr与vtable
[glibc] 带着问题看源码 —— exit 如何调用 atexit 处理器
之前在写 apue 系列的时候,曾经对系统接口的很多行为产生过好奇,当时就想研究下对应的源码,但是苦于 linux 源码过于庞杂,千头万绪不知从何开启,就一直拖了下来。
海海
2023/10/26
3730
[glibc] 带着问题看源码 —— exit 如何调用 atexit 处理器
C++ 新增的 stl 容器实用方法,你知道几个?(文末赠送 C++20 书籍)
1 原位构造与容器的emplace系列函数 在介绍emplace和emplace_back方法之前,我们先看一段代码: #include <iostream> #include <list> class Test { public:     Test(int a, int b, int c)     {         ma = a;         mb = b;         mc = c;         std::cout << "Test constructed." << std::endl;
范蠡
2022/10/08
1.1K0
C++ 新增的 stl 容器实用方法,你知道几个?(文末赠送 C++20 书籍)
C++对象的生命周期
在main中的对象依次被调用构造函数。在程序结束时,对于普通对象,采取先进后出的原则,调用析构函数。
灯珑LoGin
2022/10/31
2360
C++ 中文周刊 第130期
编译器信息最新动态推荐关注hellogcc公众号 本周没更新 上期 2023-08-30 第217期
王很水
2024/07/30
1630
C++ 中文周刊 第130期
(二)Reactor模式
最近一直在看游双的《高性能linux服务器编程》一书,下载链接: http://download.csdn.net/detail/analogous_love/9673008 书上是这么介绍React
范蠡
2018/04/04
1.7K0
(二)Reactor模式
【C/C++】——小白初步了解——内存管理
特点:如果新大小大于原大小,新分配的内存区域中的内容是不确定的;如果新大小小于原大小,超出的内容将被丢弃。
小李很执着
2024/06/15
1430
c-atexit()和_exit()
A:这是因为使用了 _exit() 方法。此方法并没有调用清除数据相关的方法,比如 atexit()等。exit和_exit都是用来正常终止一个进程的,主要区别是_exit会立刻进入内核,而exit先执行一些清除工作(包括执行各种终止处理程序,关闭所有标准I/O等,一旦关闭了IO,例如printf等函数就不会输出任何东西了),然后才进入内核。这两个函数会对父子进程有一定的影响,当用vfork创建子进程时,子进程会先在父进程的地址空间运行(这跟fork不一样),如果子进程调用了exit就会把父进程的IO给关掉。
kdyonly
2023/03/03
3330
【C++】智能指针:shared_ptr
shared_ptr的产生与unique_ptr类似,都是为了解决raw pointer的new和delete的成对使用,导致的野指针、内存泄漏、重复释放内存等。
灰子学技术
2020/12/08
2.9K0
【C++】智能指针:shared_ptr
GDB多线程多进程调试
主要包括gdb分配的线程id号(例如1,2,3),操作系统分配的线程id(例如20568),线程的名字以及线程相关的调用栈信息。
chain
2018/06/05
12.8K3
通过edge://tracing工具进行C++的可视化基准测试
在测试函数类构建一个Timer对象让他开始计时,再离开函数作用时会自动调用析构函数停止计时并且输出耗时结果
晨星成焰
2024/01/29
5842
通过edge://tracing工具进行C++的可视化基准测试
【Linux】深入理解进程控制:从创建到终止和进程等待
fork 函数是 Unix/Linux 系统中用于创建新进程的系统调用。调用 fork 后,当前进程(父进程)会被复制,创建出一个新的进程(子进程)。 fork函数特点:
用户11305458
2024/11/21
2450
【Linux】深入理解进程控制:从创建到终止和进程等待
开心档之C++ 多线程
多线程是多任务处理的一种特殊形式,多任务处理允许让电脑同时运行两个或两个以上的程序。一般情况下,两种类型的多任务处理:基于进程和基于线程。
爱学iOS的小麦子
2023/04/12
4650
【C++】自学终极笔记
2. main()函数的返回类型可以是任意的数据类型,而且是唯一一个非void型【 即void main()】可以不用return,因为main()由操作系统直接控制,不能被其他函数调用。
SarPro
2024/02/20
2710
【C++】自学终极笔记
相关推荐
C++一分钟之-构造函数与析构函数
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档