当前位置: 首页 > news >正文

全局变量初始化与销毁

先看代码:

#include <iostream> #include <memory> class Widget { public: Widget() { std::cout << "constructor" << ' '; } ~Widget() { std::cout << "destructor" << ' '; } }; std::unique_ptr<Widget> widget = std::make_unique<Widget>(); int main() { std::cout << "main" << ' '; return 0; }

这段代码会输出什么?

先说答案,会输出:constructor main destructor

为什么呢?

一、初始化:分两个阶段,不是一次完成

C++ 标准[basic.start.static]/[basic.start.dynamic]把全局/静态对象的初始化分为:

阶段时机包含内容

静态初始化

程序启动最早期(main 之前很久)

零初始化、常量初始化(如int x = 42;constexpr对象)

动态初始化

main 之前,但可能延迟到首次使用

需要运行代码的初始化(如调用构造函数、int x = f();

举例:

int a = 0; int b = 42; int c = some_func(); Widget w; ```

二、构造顺序:同一文件内确定,跨文件未定义

```cpp

// file_a.cpp

A globalA;

// file_b.cpp

B globalB;

globalAglobalB谁先构造是未定义的——这就是著名的 Static Initialization Order Fiasco (SIOF)。如果B的构造函数依赖A已经构造好,就会踩坑。

解决方案是 Construct On First Use 惯用法(用函数内的static变量):

A& getGlobalA() {

static A instance;

return instance;

}

三、释放:不是简单的"main 之后"

更准确的说法是:

全局对象的析构函数在main正常返回之后,或调用std::exit()时执行。

注意三种不会调用析构函数的退出方式:

退出方式是否调析构

return 0;from main

✅ 会

std::exit(0)

✅ 会

std::abort()

❌ 不会

std::_Exit()/_exit()

❌ 不会

std::quick_exit()

❌ 不会(只调at_quick_exit注册的函数)

未捕获异常逃出 main

⚠️ 实现定义(通常是 abort,不调析构)

析构顺序与构造顺序严格相反(LIFO)。

四、和atexit的交互

#include <cstdlib>

#include <iostream>

struct S {

S() { std::cout << "S ctor\n"; }

~S() { std::cout << "S dtor\n"; }

};

S s;

int main() {

std::atexit([]{ std::cout << "atexit\n"; });

return 0;

}

输出:

S ctor

atexit

S dtor

规则:atexit注册的函数和全局对象的析构按"逆构造顺序"统一编排——谁后注册/构造,谁先执行。所以atexit在 main 末尾才注册,但它先于s的析构执行。


五、写个完整的验证程序

#include <iostream>

#include <cstdlib>

struct Tracer {

const char* name;

Tracer(const char* n) : name(n) { std::cout << "ctor " << name << '\n'; }

~Tracer() { std::cout << "dtor " << name << '\n'; }

};

Tracer g1("g1");

Tracer g2("g2");

Tracer* heap = new Tracer("heap");

int main() {

std::cout << "--- enter main ---\n";

static Tracer s1("s1");

std::atexit([]{ std::cout << "atexit handler\n"; });

std::cout << "--- leave main ---\n";

return 0;

}

预期输出:

ctor g1

ctor g2

ctor heap

--- enter main ---

ctor s1

--- leave main ---

atexit handler

dtor s1

dtor g2

dtor g1

可以观察到:

  1. g1g2heap在 main 前构造(堆对象的指针赋值算动态初始化)
  2. 函数内static变量s1是首次执行到时才构造(C++11 起线程安全)
  3. 析构顺序严格逆序:s1g2g1
  4. heap的析构没出现——印证上一个问题的结论
  5. atexit在所有析构之前执行(因为它最后注册)

对于如下代码:

#include <iostream> class Widget { public: Widget(){ std::cout << "constructor" << ' '; } ~Widget(){ std::cout << "destructor" << ' '; } }; Widget *widget = new Widget; int main() { std::cout << "main" << ' '; return 0; }

代码输出:

constructor main

核心原因:C++ 标准规定new创建的对象必须显式delete才会调用析构函数。程序退出时操作系统只是回收进程的物理内存(mmap/brk 释放),并不会逐个调用 C++ 对象的析构函数——这是典型的内存泄漏(虽然程序马上要退出了所以不会造成实际危害)。

所以输出就是:constructor main,缺少destructor

如果我们在main函数中加入:

delete widget;

#include <iostream> class Widget { public: Widget(){ std::cout << "constructor" << ' '; } ~Widget(){ std::cout << "destructor" << ' '; } }; Widget *widget = new Widget; int main() { std::cout << "main" << ' '; delete widget; return 0; }

则会输出:

constructor main destructor

http://www.cnnetsun.cn/news/2123479.html

相关文章:

  • 突破GitHub1s性能瓶颈:大型仓库秒开优化终极指南
  • 深度Delta学习与Householder反射在Transformer中的应用
  • EncFS加密文件系统入门:5分钟学会创建你的第一个安全存储空间
  • React Native Draggable FlatList与Swipeable Item集成:实现多功能交互列表
  • Ant Design Charts 与 TypeScript 完美结合:类型安全的图表开发最佳实践
  • 大语言模型在知识图谱验证中的性能评估与优化策略
  • 构建漏洞银行MCP系统与自动化攻击测试实践
  • Phi-3.5-mini-instruct镜像免配置:预置多语言测试用例一键验证
  • 鉴权基础扫盲:Session、Cookie、Token、JWT、OAuth 2.0核心概念辨析
  • 量子计算威胁下的密码学革新与PQC实践
  • YOLO26电梯内电动车识别检测系统(项目源码+YOLO数据集+模型权重+UI界面+python+深度学习+远程环境部署)
  • 当Attention遇见矩阵乘法:一个被忽视的真相
  • RoPE频谱放大与Transformer位置编码优化实践
  • 合成人脸嵌入向量技术:原理、实现与应用
  • 基于Java Web的驾校考试管理系统架构总结【答辩文档】
  • 【多线路故障】含sop的配电网故障重构研究(Matlab代码实现)
  • Linux入门】VMware安装CentOS 7超详细图文教程(附常见问题解决)
  • 3步解锁华硕笔记本隐藏性能:G-Helper轻量级硬件控制全指南
  • 【2026版】最新c语言入门,零基础入门到精通,收藏这篇就够了
  • GPT-2文本生成实战:从原理到应用
  • EDAN工具解析:HPC内存优化与执行DAG分析
  • 生产环境离线部署 MongoDB 高可用集群(CentOS系统)
  • 告别网盘限速:八大平台直链解析工具完全指南
  • 基于轨迹跟踪的侧倾与曲率变化修正:Simulink与Carsim联合仿真技术探讨
  • 【20年IDE生态专家实测】:Copilot Next 工作流配置面试通关路径图——含YAML Schema校验、权限沙箱、Telemetry埋点3大权威验证项
  • 深度学习模型评估:交叉验证与置信区间
  • AI写论文宝藏推荐!4款AI论文生成工具,助力撰写高质量论文!
  • 【Redis实战篇 | Day04】Lua原子性优化Redis分布式锁:解决线程安全问题
  • 保姆级教程:用DriveAct数据集复现自动驾驶行为识别实验(附代码与避坑指南)
  • Windows 11系统优化终极指南:用Win11Debloat告别臃肿与隐私泄露