Effective C++ 学习笔记 条款30 透彻了解inlining的里里外外
内联函数——多么美妙的想法它们长得像函数行为像函数比宏强太多了参见条款2而且调用它们还无需承担函数调用的开销。你还能奢求什么呢实际上你能得到的好处还不止这些因为免除函数调用开销只是故事的一部分。编译器优化通常针对不含函数调用的代码段进行所以当你内联一个函数时编译器就有机会结合调用上下文对函数体做针对性的优化。大多数编译器对“非内联”的函数调用是不会做这类优化的。然而编程跟生活一样没有免费的午餐内联函数也不例外。内联的基本思路是用函数体代码替换每一次函数调用用不着统计学博士也能明白这很可能会增加目标代码的体积。在内存有限的机器上过度热衷内联可能导致程序体积超过可用空间。即便有虚拟内存内联带来的代码膨胀也可能引发额外的分页操作、降低指令缓存命中率从而拖累性能。反过来如果内联函数体非常短小为函数体生成的代码可能比一次函数调用本身还小。如果是这种情况内联反而有可能让目标代码变得更小指令缓存命中率更高请记住inline 只是对编译器的一个请求而非命令。这个请求可以隐式提出也可以显式提出。隐式方式是在类定义内部定义函数这类函数通常是成员函数但条款46指出友元函数也可以定义在类内部。如果这样做了它们也同样会被隐式声明为内联。显式声明内联函数的方式是在函数定义前加上 inline 关键字。例如标准库中的 max 模板来自algorithm通常就是这样实现的max 是个模板这一点引出了一个常见现象内联函数和模板通常都定义在头文件中。于是有些程序员就得出结论认为函数模板必须声明为 inline。这个结论既不正确还可能带来麻烦所以我们有必要稍作探讨。内联函数通常必须放在头文件中因为大多数构建环境是在编译期完成内联展开的——编译器要把函数调用替换成函数体就得先知道函数长什么样当然有些构建环境可以在链接期做内联极少数——比如基于 .NET 公共语言基础结构CLI的托管环境——甚至能在运行时内联但这些都是例外情况大多数 C 程序中的内联都是在编译期进行的。模板通常也放在头文件中因为编译器在实例化模板时需要知道它的定义同样这并非放之四海皆准——有些构建环境在链接期才做模板实例化但编译期实例化更为普遍。模板实例化与内联是两码事。如果你在写一个模板并且认为从该模板实例化出的所有函数都应该内联那就把模板声明为 inline——上面 std::max 的实现正是这么做的。但如果你写的模板函数没什么理由要内联就不要声明该模板为 inline无论是显式还是隐式。内联有代价你不该在没有深思熟虑之前就贸然承担它。我们前面提到过内联会导致代码膨胀这对模板作者尤其需要重视——参见条款44但它还有其他代价我们稍后会谈到。在此之前先说完一个事实inline 只是请求编译器可以忽略。大多数编译器拒绝内联它们认为过于复杂的函数比如包含循环或递归的而且除了最微不足道的虚函数调用外几乎所有的虚函数都无法内联。后者并不让人意外——virtual 意味着“等到运行时再决定调用哪个函数”而 inline 意味着“执行之前就把调用点替换成函数体”。如果编译器根本不知道会调用哪个函数它拒绝内联该函数体也情有可原。综合以上种种可以得出一个结论一个内联函数是否真的被内联取决于你使用的构建环境——主要是编译器。幸运的是大多数编译器都有一个诊断级别如果你请求内联的函数最终未被内联它会发出警告参见条款53。有时即使编译器完全愿意内联某个函数它仍然会为该函数生成一个独立的函数体。比如如果你的程序取了某个内联函数的地址编译器通常就必须为它生成一个非内联的函数体——它们怎么给一个不存在的函数提供指针呢再加上编译器通常不会对通过函数指针的调用执行内联这就意味着对内联函数的调用是否会被内联取决于调用的方式即使你从不使用函数指针未被内联的内联函数也可能给你带来麻烦因为索取函数指针的不一定是你——编译器有时也会生成构造函数和析构函数的非内联副本以便在数组对象的构造和析构过程中获取这些函数的指针来使用。事实上构造函数和析构函数往往比乍看之下更不适合内联。比如考虑下面 Derived 类的构造函数这个构造函数看起来是个绝佳的内联候选因为它里面没有代码。但表象会骗人。C 对对象创建和销毁时会发生的事情做了各种保证。比如你使用 new 时动态创建的对象会自动被其构造函数初始化使用 delete 时对应的析构函数会被调用。当你创建一个对象时该对象的每个基类子对象和每个数据成员都会被自动构造销毁对象时则会自动执行相反的过程。如果在构造对象的过程中抛出异常任何已经构造完成的部分都会被自动销毁。在所有这些场景中C 规定了“什么必须发生”但没说“如何发生”。那是编译器实现者的事但有一点很清楚这些事不会自己凭空发生。你的程序里必须有某些代码来完成它们而那些代码——由编译器编写、在编译期间插入到程序中的代码——总得有个去处。有时它们就落到了构造函数和析构函数里所以我们可以想象上面那个看似为空的 Derived 构造函数其实现代码可能等效于下面这样这段代码并不代表真正编译器生成的实现因为实际编译器会以更巧妙的方式处理异常。但它确实准确地反映了 Derived 这个“空”构造函数必须提供的行为。无论编译器的异常实现多么精巧Derived 的构造函数至少还得去调用其数据成员和基类的构造函数而这些调用它们本身也可能被内联或多或少会影响该构造函数作为内联候选的吸引力。同理也适用于 Base 的构造函数所以如果基类构造函数被内联了那么插入到它里面的所有代码也会通过 Derived 构造函数对基类构造函数的调用而一并插入到 Derived 构造函数中。而如果 string 的构造函数恰好也被内联那么 Derived 构造函数就会获得五份该函数的代码副本——对应 Derived 对象中的五个 string继承自基类的两个加上自己声明的三个。现在你应该明白了是否把 Derived 的构造函数声明为内联并不是一个想当然就能拍板的决定。同样的情况也适用于 Derived 的析构函数——它无论如何都必须确保所有由 Derived 构造函数初始化过的对象能被正确地销毁。库的设计者在决定将函数声明为 inline 时必须评估其影响因为一旦库中某个客户端可见的内联函数被改动就无法提供“二进制升级”——换句话说如果库里的 f 是内联函数那么库的客户会把 f 的函数体编译到他们自己的应用程序中。若库的实现者后来决定修改 f所有使用过 f 的客户都必须重新编译。这往往不是人们想要的。反过来如果 f 是非内联函数修改 f 只需客户重新链接即可——这个负担比重新编译轻得多而且如果包含该函数的库是动态链接的甚至可以做到对客户完全透明。就程序开发而言把这些考量都放在心上很重要。但从编码的实用角度来看有一个事实压倒其他一切大多数调试器对内联函数都束手无策。这其实并不令人意外——你怎么在一个不存在的函数里设置断点呢虽然有些构建环境设法支持对内联函数的调试但很多环境在调试构建时干脆直接禁用了内联。由此可以得出一个合理的策略来判断哪些函数应该声明为内联哪些不应该一开始先不要内联任何东西或者至少把内联限制在那些必须内联参见条款46或极其简单的函数上比如第135页的 Person::age。谨慎使用内联既能方便你用调试器也能把内联放在它该在的位置——一种手工施加的优化。别忘了那条经验法则“80-20法则”一个典型程序会把 80% 的执行时间花在仅 20% 的代码上。这条法则很重要因为它提醒你作为软件开发者的目标是找出那 20% 能提升程序整体性能的代码。你可以对内联函数做各种调优但如果没找对目标函数那都是白费力气。切记1.大多数内联应仅限于小型、频繁调用的函数。这样做既方便调试也利于二进制升级还能最大程度避免代码膨胀并最有可能提升程序运行速度。2.不要仅仅因为函数模板出现在头文件中就把它们声明为 inline。

相关新闻

ESXi 7.0 嵌套虚拟化与显卡直通冲突:3种配置方案实测与稳定性对比

ESXi 7.0 嵌套虚拟化与显卡直通冲突:3种配置方案实测与稳定性对比

ESXi 7.0嵌套虚拟化与显卡直通冲突的深度解析与实战方案1. 技术背景与核心冲突机制在虚拟化环境中同时启用嵌套虚拟化和PCIe直通功能,本质上是在挑战硬件资源的独占性与共享性边界。ESXi 7.0的vhv.enable参数与PCI直通功能存在底层架构的互斥性,这种冲突…

2026/7/12 2:12:06 阅读更多 →
基于TC78H653FTG和PIC18F46K42的直流有刷电机控制方案

基于TC78H653FTG和PIC18F46K42的直流有刷电机控制方案

1. 直流有刷电机控制方案概述在工业自动化和消费电子领域,直流有刷电机因其结构简单、成本低廉和控制方便等优势,仍然是许多应用场景的首选驱动方案。然而,传统的驱动方式往往存在效率低下、控制精度不足等问题。本文将详细介绍如何利用东芝的…

2026/7/12 2:12:06 阅读更多 →
如何构建跨平台QSP游戏引擎解决本地化运行难题

如何构建跨平台QSP游戏引擎解决本地化运行难题

如何构建跨平台QSP游戏引擎解决本地化运行难题 【免费下载链接】JavaQuestPlayer 项目地址: https://gitcode.com/gh_mirrors/ja/JavaQuestPlayer 在游戏开发领域,特别是独立游戏和视觉小说创作中,QSP(Quest Soft Player)…

2026/7/12 2:10:05 阅读更多 →

最新新闻

Hadoop 3.1.3 安装源对比:华为/清华/北外3大镜像下载速度与版本覆盖实测

Hadoop 3.1.3 安装源对比:华为/清华/北外3大镜像下载速度与版本覆盖实测

Hadoop 3.1.3 国内镜像站深度评测:华为/清华/北外三大源实测指南当你在阿里云ECS上部署Hadoop集群时,第一个拦路虎往往是安装包的下载速度。国内开发者常遇到官方源连接超时、海外镜像速度缓慢等问题。本文将针对华为云、清华大学和北京外国语大学三大主…

2026/7/12 2:58:17 阅读更多 →
epkg-factory未来展望:OpenEuler包管理生态的发展趋势

epkg-factory未来展望:OpenEuler包管理生态的发展趋势

epkg-factory未来展望:OpenEuler包管理生态的发展趋势 【免费下载链接】epkg-factory auto create/update epkg packages and repos 项目地址: https://gitcode.com/openeuler/epkg-factory 前往项目官网免费下载:https://ar.openeuler.org/ar/ …

2026/7/12 2:58:17 阅读更多 →
吴恩达深度学习调优课:超参数、正则化与优化算法实战指南

吴恩达深度学习调优课:超参数、正则化与优化算法实战指南

这次我们来看吴恩达深度学习课程第二门《改善深层神经网络:超参数调优、正则化与优化》的中文语音版本。这个资源对于想要系统学习深度学习调优技巧的开发者来说是个宝藏,特别是那些已经掌握了基础神经网络知识,想要进一步提升模型性能的读者…

2026/7/12 2:56:16 阅读更多 →
DC-DC升压转换器设计与STM32智能控制实现

DC-DC升压转换器设计与STM32智能控制实现

1. 项目背景与核心器件选型在电力电子设计中,DC-DC升压转换是一个经典课题。当我们需要将较低电压(如3.3V或5V)提升到更高电压(如12V、24V甚至38V)时,升压转换器(Boost Converter)是最常用的解决方案。本次…

2026/7/12 2:54:15 阅读更多 →
SRAM 与 DRAM 物理结构对比:6 晶体管 vs 1 晶体管 1 电容的成本与速度博弈

SRAM 与 DRAM 物理结构对比:6 晶体管 vs 1 晶体管 1 电容的成本与速度博弈

SRAM与DRAM的晶体管级博弈:6T与1T1C结构如何重塑存储金字塔在计算机体系结构中,存储器的速度与容量始终是一对难以调和的矛盾。当我们深入晶体管层面观察,会发现SRAM(静态随机存取存储器)和DRAM(动态随机存…

2026/7/12 2:54:15 阅读更多 →
Windows虚拟机安装Claude Science:完整Linux环境搭建指南

Windows虚拟机安装Claude Science:完整Linux环境搭建指南

如果你是一名Windows用户,最近被各种AI编程工具刷屏,却发现自己无法直接运行某些Linux环境下的工具,那么这篇文章就是为你准备的。Claude Science作为Anthropic推出的科研编程助手,在代码生成、数据分析、科学计算等方面表现出色&…

2026/7/12 2:50:14 阅读更多 →

日新闻

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨 在一个阳光明媚的上午,互联网大厂的面试官坐在桌前,准备迎接他的面试候选人——燕双非,一个以搞笑和幽默著称的程序员。第一轮提问 面试官:燕双非,作…

2026/7/12 0:03:13 阅读更多 →
车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估在智能驾驶和车联网技术快速发展的今天,车载以太网作为新一代车载网络的核心传输技术,其物理层性能直接决定了数据传输的可靠性和稳定性。1000BASE-T1作为当前主流的…

2026/7/12 0:03:14 阅读更多 →
VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战指南嵌入式开发领域正经历一场工具链的静默革命。当传统Keil用户首次打开VSCode的扩展市场搜索EIDE时,往往会惊讶于这个看似简单的插件竟能重构十余年的开发习惯。本文将揭示如何用五个精准步骤&#xff0…

2026/7/12 0:03:14 阅读更多 →

周新闻

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨 在一个阳光明媚的上午,互联网大厂的面试官坐在桌前,准备迎接他的面试候选人——燕双非,一个以搞笑和幽默著称的程序员。第一轮提问 面试官:燕双非,作…

2026/7/12 0:03:13 阅读更多 →
车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估在智能驾驶和车联网技术快速发展的今天,车载以太网作为新一代车载网络的核心传输技术,其物理层性能直接决定了数据传输的可靠性和稳定性。1000BASE-T1作为当前主流的…

2026/7/12 0:03:14 阅读更多 →
VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战指南嵌入式开发领域正经历一场工具链的静默革命。当传统Keil用户首次打开VSCode的扩展市场搜索EIDE时,往往会惊讶于这个看似简单的插件竟能重构十余年的开发习惯。本文将揭示如何用五个精准步骤&#xff0…

2026/7/12 0:03:14 阅读更多 →

月新闻