C++ inline用法

2023-11-01

1、引入 inline 关键字的原因

在 c/c++ 中,为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)的问题,特别的引入了 inline 修饰符,表示为内联函数。

栈空间就是指放置程序的局部数据(也就是函数内数据)的内存空间。

在系统下,栈空间是有限的,假如频繁大量的使用就会造成因栈空间不足而导致程序出错的问题,如,函数的死循环递归调用的最终结果就是导致栈内存空间枯竭。

下面我们来看一个例子:

 inline const char *num_check(int v)
{
    return (v % 2 > 0) ? "奇" : "偶";
}
 
int main(void)
{
    int i;
    for (i = 0; i < 100; i++)
        printf("%02d   %s\n", i, num_check(i));
    return 0;
}

上面的例子就是标准的内联函数的用法,使用 inline 修饰带来的好处我们表面看不出来,其实,在内部的工作就是在每个 for 循环的内部任何调用 num_check(i) 的地方都换成了 (i%2>0)?"奇":"偶",这样就避免了频繁调用函数对栈内存重复开辟所带来的消耗。

2、inline使用限制

inline 的使用是有所限制的,inline 只适合函数体内代码简单的函数使用,不能包含复杂的结构控制语句例如 while、switch,并且不能内联函数本身不能是直接递归函数(即,自己内部还调用自己的函数)。

3、inline仅是一个对编译器的建议

inline 函数仅仅是一个对编译器的建议,所以最后能否真正内联,看编译器的意思,它如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就会内联,声明内联只是一个建议而已。

4、建议 inline 函数的定义放在头文件中

其次,因为内联函数要在调用点展开,所以编译器必须随处可见内联函数的定义,要不然就成了非内联函数的调用了。所以,这要求每个调用了内联函数的文件都出现了该内联函数的定义

因此,将内联函数的定义放在头文件里实现是合适的,省却你为每个文件实现一次的麻烦。

声明跟定义要一致:如果在每个文件里都实现一次该内联函数的话,那么,最好保证每个定义都是一样的,否则,将会引起未定义的行为。如果不是每个文件里的定义都一样,那么,编译器展开的是哪一个,那要看具体的编译器而定。所以,最好将内联函数定义放在头文件中。

5、类中的成员函数与inline

定义在类中的成员函数默认都是内联的,如果在类定义时就在类内给出函数定义,那当然最好。如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上 inline,否则就认为不是内联的。

class A
{
    public:void Foo(int x, int y) {  } // 自动地成为内联函数
}

将成员函数的定义体放在类声明之中虽然能带来书写上的方便,但不是一种良好的编程风格,上例应该改成:

// 头文件
class A
{
    public:
    void Foo(int x, int y);
}
 
 
// 定义文件
inline void A::Foo(int x, int y){}

6、inline 是一种"用于实现的关键字"

关键字 inline 必须与函数定义体放在一起才能使函数成为内联,仅将 inline 放在函数声明前面不起任何作用。

如下风格的函数 Foo 不能成为内联函数:

inline void Foo(int x, int y); // inline 仅与函数声明放在一起
void Foo(int x, int y){}

而如下风格的函数 Foo 则成为内联函数:

void Foo(int x, int y);
inline void Foo(int x, int y) {} // inline 与函数定义体放在一起

所以说,inline 是一种"用于实现的关键字",而不是一种"用于声明的关键字"。一般地,用户可以阅读函数的声明,但是看不到函数的定义。尽管在大多数教科书中内联函数的声明、定义体前面都加了inline 关键字,但我认为inline不应该出现在函数的声明中。这个细节虽然不会影响函数的功能,但是体现了高质量C++/C 程序设计风格的一个基本原则:声明与定义不可混为一谈,用户没有必要、也不应该知道函数是否需要内联。

7、慎用 inline

内联能提高函数的执行效率,为什么不把所有的函数都定义成内联函数?如果所有的函数都是内联函数,还用得着"内联"这个关键字吗? 
内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。 
如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。

以下情况不宜使用内联: 
(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。 
(2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如"偷偷地"执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明了 inline 不应该出现在函数的声明中)。

8、总结

内联函数并不是一个增强性能的灵丹妙药。只有当函数非常短小的时候它才能得到我们想要的效果;但是,如果函数并不是很短而且在很多地方都被调用的话,那么将会使得可执行体的体积增大。 
最令人烦恼的还是当编译器拒绝内联的时候。在老的实现中,结果很不尽人意,虽然在新的实现中有很大的改善,但是仍然还是不那么完善的。一些编译器能够足够的聪明来指出哪些函数可以内联哪些不能,但是大多数编译器就不那么聪明了,因此这就需要我们的经验来判断。如果内联函数不能增强性能,就避免使用它

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++ inline用法 的相关文章

  • 如何将 std::string& 转换为 C# 引用字符串

    我正在尝试将 C 函数转换为std string参考C 我的 API 如下所示 void GetStringDemo std string str 理想情况下 我希望在 C 中看到类似的东西 void GetStringDemoWrap r
  • 调用 McAfee 病毒扫描引擎

    我收到客户的请求 要求使用他们服务器上的 McAfee 病毒扫描将病毒扫描集成到应用程序中 我做了一些调查 发现 McScan32 dll 是主要的扫描引擎 它导出各种看起来有用的函数 我还发现提到了 McAfee Scan Engine
  • STL 迭代器:前缀增量更快? [复制]

    这个问题在这里已经有答案了 可能的重复 C 中的预增量比后增量快 正确吗 如果是 为什么呢 https stackoverflow com questions 2020184 preincrement faster than postinc
  • C# 异步等待澄清?

    我读了here http blog stephencleary com 2012 02 async and await html that 等待检查等待的看看它是否有already完全的 如果 可等待已经完成 那么该方法将继续 运行 同步
  • std::list 线程push_back、front、pop_front

    std list 线程安全吗 我假设不是这样 所以我添加了自己的同步机制 我认为我有正确的术语 但我仍然遇到问题 每个函数都由单独的线程调用 Thread1 不能等待 它必须尽可能快 std list
  • 如何在 C# 中打开 Internet Explorer 属性窗口

    我正在开发一个 Windows 应用程序 我必须向用户提供一种通过打开 IE 设置窗口来更改代理设置的方法 Google Chrome 使用相同的方法 当您尝试更改 Chrome 中的代理设置时 它将打开 Internet Explorer
  • 如何使从 C# 调用的 C(P/invoke)代码“线程安全”

    我有一些简单的 C 代码 它使用单个全局变量 显然这不是线程安全的 所以当我使用 P invoke 从 C 中的多个线程调用它时 事情就搞砸了 如何为每个线程单独导入此函数 或使其线程安全 我尝试声明变量 declspec thread 但
  • 需要帮助优化算法 - 两百万以下所有素数的总和

    我正在尝试做一个欧拉计划 http projecteuler net问题 我正在寻找 2 000 000 以下所有素数的总和 这就是我所拥有的 int main int argc char argv unsigned long int su
  • 访问外部窗口句柄

    我当前正在处理的程序有问题 这是由于 vista Windows 7 中增强的安全性引起的 特别是 UIPI 它阻止完整性级别较低的窗口与较高完整性级别的窗口 对话 就我而言 我想告诉具有高完整性级别的窗口进入我们的应用程序 它在 XP 或
  • ASP.NET Core 3.1登录后如何获取用户信息

    我试图在登录 ASP NET Core 3 1 后获取用户信息 如姓名 电子邮件 id 等信息 这是我在登录操作中的代码 var claims new List
  • C# 列表通用扩展方法与非通用扩展方法

    这是一个简单的问题 我希望 集合类中有通用和非通用方法 例如List
  • 如何获取 EF 中与组合(键/值)列表匹配的记录?

    我有一个数据库表 其中包含每个用户 年份组合的记录 如何使用 EF 和用户 ID 年份组合列表从数据库获取数据 组合示例 UserId Year 1 2015 1 2016 1 2018 12 2016 12 2019 3 2015 91
  • C# - 当代表执行异步任务时,我仍然需要 System.Threading 吗?

    由于我可以使用委托执行异步操作 我怀疑在我的应用程序中使用 System Threading 的机会很小 是否存在我无法避免 System Threading 的基本情况 只是我正处于学习阶段 例子 class Program public
  • 为什么 C# 2.0 之后没有 ISO 或 ECMA 标准化?

    我已经开始学习 C 并正在寻找标准规范 但发现大于 2 0 的 C 版本并未由 ISO 或 ECMA 标准化 或者是我从 Wikipedia 收集到的 这有什么原因吗 因为编写 审查 验证 发布 处理反馈 修订 重新发布等复杂的规范文档需要
  • C# xml序列化必填字段

    我需要将一些字段标记为需要写入 XML 文件 但没有成功 我有一个包含约 30 个属性的配置类 这就是为什么我不能像这样封装所有属性 public string SomeProp get return someProp set if som
  • C 编程:带有数组的函数

    我正在尝试编写一个函数 该函数查找行为 4 列为 4 的二维数组中的最大值 其中二维数组填充有用户输入 我知道我的主要错误是函数中的数组 但我不确定它是什么 如果有人能够找到我出错的地方而不是编写新代码 我将不胜感激 除非我刚去南方 我的尝
  • 为什么使用小于 32 位的整数?

    我总是喜欢使用最小尺寸的变量 这样效果就很好 但是如果我使用短字节整数而不是整数 并且内存是 32 位字可寻址 这真的会给我带来好处吗 编译器是否会做一些事情来增强内存使用 对于局部变量 它可能没有多大意义 但是在具有数千甚至数百万项的结构
  • 如何实例化 ODataQueryOptions

    我有一个工作 简化 ODataController用下面的方法 public class MyTypeController ODataController HttpGet EnableQuery ODataRoute myTypes pub
  • DotNetZip:如何提取文件,但忽略zip文件中的路径?

    尝试将文件提取到给定文件夹 忽略 zip 文件中的路径 但似乎没有办法 考虑到其中实现的所有其他好东西 这似乎是一个相当基本的要求 我缺少什么 代码是 using Ionic Zip ZipFile zf Ionic Zip ZipFile
  • 指针和内存范围

    我已经用 C 语言编程有一段时间了 但对 C 语言还是很陌生 有时我对 C 处理内存的方式感到困惑 考虑以下有效的 C 代码片段 const char string void where is this pointer variable l

随机推荐

  • 将硬盘转换成GPT分区格式

    首先你要知道并非只有更换成这种格式才能安装WIN10这样的新系统 只有在硬盘容量为3TB以上 而你又想往上面装操作系统时 才会涉及到GPT分区 若是往小于2 2TB的硬盘上装系统 则无须使用GPT格式 使用MBR分区 照样可以正常使用WIN
  • Py的docx库:Python操作docx文件的详细教程

    Py的docx库 Python操作docx文件的详细教程 docx库是一种Python库 它使得在Python中提取 编辑和创建Microsoft Word 2007 docx文件变得容易 这个库具有非常强大的功能 可以处理Word文档中的
  • php连接mysql数据库报No such file or directory错误

    mysql connect 连接数据库时报No such file or directory错误 原因是mac下的默认php ini配置default socket 在 var mysql mysql socket 而后安装的mysql的s
  • 计算机 游戏 排名2015年,2015十大高性能游戏笔记本电脑排行 最强游戏本推荐

    电脑游戏拥有千万玩家 在全球市场实际上比游戏主机更受欢迎 当然 工欲善其事必先利其器 一台高性能PC是快乐游戏的基础 一些拥有较好电脑知识 善于动手的用户可以选择自行DIY一台台式游戏PC 但这显然不适合所有人 所以 高性能游戏笔记本也成为
  • 一个男人的自白

    十岁以前 就不说了 无非是淘气和不懂事 十三四岁的时候 开始对女孩有好感 但是却离女孩远远的 并且以讨厌女孩自居 十五岁的时候 听别人说某某把女朋友甩了 女孩自杀了 他想 自己将来一定要做个痴情的男人 一生只爱一个人 十六岁的时候 他喜欢上
  • 数据库的死锁问题在设计期就可以避免

    前几天偶尔与一位数据库工程师谈起数据库的死锁 deadlock 问题 根据以往的经验 我一直认为 1 MSSQL DB2 Oracle之类的现代DBMS或者中间件可以帮助我们自动解决绝大部分死锁 其余一部分难以处理的死锁则由DBA在数据库控
  • Leetcode--无重复字符的最长子串(思路+解题逻辑+详细代码注释)

    题目 给定一个字符串 s 请你找出其中不含有重复字符的 最长子串 的长度 示例 1 输入 s abcabcbb 输出 3 解释 因为无重复字符的最长子串是 abc 所以其长度为 3 示例 2 输入 s pwwkew 输出 3 解释 因为无重
  • 02 - MATLAB基础

    目录 一 matlab系统环境 二 MATLAB数值数据 二 变量与赋值 四 MATLAB矩阵的表示 五 矩阵元素的引用 六 MATLAB基本运算 七 字符串处理 注 2 6节为数值型数据 第7节为字符型数据 一 matlab系统环境 1
  • YOLO V5 使用

    目录 前言 环境 1 安装python所需的数据库 2 采集图片样本 2 1勾选View内容 2 2切换至YOLO模式 2 3打开训练图集文件以及labels文件 2 4建立类名 2 5开始标记 3 训练模型 4 模型预测 5 预测结果的返
  • Flutter 1-10】Flutter手把手教程Dart语言——运算符

    作者 弗拉德 来源 弗拉德 公众号 fulade me 运算符 运算符是一种告诉编译器执行特定的数学或逻辑操作的符号 Dart语言内置了丰富的运算符 并提供了以下类型的运算符 算术运算符 关系运算符 类型判断运算符 赋值运算符 逻辑运算符
  • 什么浏览器好用_手机浏览器不只UC,好用的浏览器还有这些

    在手机上 很多人都用UC 当然也有不少人使用的是系统预装的浏览器 其实除了这两种之外 还有很多良心浏览器你没见过 今天就给大家介绍下五款安卓平台上那些比较良心而且相对好用的浏览器 一 X浏览器 这个浏览器非常小 仅800K左右 也不需要很多
  • java 手机号运营商号段正则匹配(长期更新)

    import com cq mysmscommon enums SpTypeEn import java util regex Pattern public class MobileOperator 中国移动号码正则 139 138 137
  • 【Qt】Qt中智能指针

    文章目录 一 QPointer 二 QSharedPointer 2 1 常用操作函数 1 返回此对象所引用的指针的值 2 清除这个QSharedPointer对象 删除它可能对该指针的引用 如果这是最后一个引用 那么指针本身将被删除 3
  • Archery 部署使用命令--(外置mysql和redis)

    mysqld safe socket data dockermysql mysql sock nice 0 mysqld pid file data dockermysql mysql pid socket data dockermysql
  • linux客户端工具_详解远程桌面协议, Linux 和 Windows 间的远程桌面互相访问(RDP、VNC协议)...

    前言 远程协议有很多 本文只讨论几种主要的 另外由于Windows之间的远程桌面工具实在是太多了 这里就不再说明 主要以Windows和Linux之间的互相访问来说明 一 常见的远程协议及特点 目前常用的协议有VNC SPICE RDP三种
  • 英飞凌单片机编译器 TASKING TriCore Eclipse IDE 新建静态库工程

    前言 这篇介绍一下如何使用 TASKING 新建一个静态库的工程 编译成一个静态库 最后链接至应用程序工程中进行编译调试 编译成静态库也能debug界面调试 也可以打断点操作等 前提是保证源码和编译的静态库源码一致 且含有调试信息 下载 A
  • Android常用正则

    1 匹配以特定字符开头 特定字符结尾 private const val AT s S 匹配以 打头 空格结尾的字符 2 匹配手机号 const val REG PHONE 1 0 9 10 3 匹配判断是否汉字字母数字和 const va
  • Centos 7 freeradius 搭建企业wifi认证服务

    Centos 7 搭建Wpa认证服务 关键字 freeradius wpa eap 参考 http www racksam com 2017 03 02 centos7 install freeradius 路由器设置为WPA WPA2企业
  • 阿里Sentinel控制台源码修改-对接Apollo规则持久化

    改造背景 前面我们讲解了如何对接Apollo来持久化限流的规则 对接后可以直接通过Apollo的后台进行规则的修改 推送到各个客户端实时生效 但还有一个问题就是Sentinel控制台没有对接Apollo Sentinel控制台本来就可以修改
  • C++ inline用法

    1 引入 inline 关键字的原因 在 c c 中 为了解决一些频繁调用的小函数大量消耗栈空间 栈内存 的问题 特别的引入了 inline 修饰符 表示为内联函数 栈空间就是指放置程序的局部数据 也就是函数内数据 的内存空间 在系统下 栈