C++ 函数语义学——inline 函数回顾和扩展

news/2025/1/21 15:02:31/

inline 函数回顾和扩展

inline 函数回顾和扩展

    • inline 函数回顾和扩展
      • 1. inline 函数回顾
      • 2. inline 扩展
      • 总结

1. inline 函数回顾

inline 函数即有优点又有缺点,优点是它的执行成本一般比常规的函数调用和函数返回所带来的成本低,提高了程序执行效率,但缺点是会导致代码的膨胀。

使用 inline 函数需要知道的几个关键点:

  1. 关键字 inline 只是对编译器的一个建议

    • 如果编译器评估这个 inline 函数的复杂度过高,那可能这个 inline 建议就无效了,编译器会产生常规的函数定义和调用代码。
  2. inline 函数的扩展

    • 如果 inline 被编译器采用,那么 inline 函数的扩展就要在调用这个 inline 函数的那个点上进行,这就可能会带来额外的问题,如参数的求值、临时对象的产生和管理等。
  3. 定义和声明

    • inline 函数通常在头文件中定义,以便在多个翻译单元中使用时保持一致性。
    • 如果在头文件中定义 inline 函数,确保每个包含该头文件的翻译单元都能看到相同的函数定义。
    // header.h
    inline int add(int a, int b) {return a + b;
    }
    
  4. 编译器优化

    • 编译器可能会根据具体情况自动将某些函数内联,即使没有显式使用 inline 关键字。
    • 反之,即使显式使用了 inline 关键字,编译器也可能选择不内联某些函数。
  5. 内联函数与宏的比较

    • inline 函数比宏更安全,因为它们遵循 C++ 的作用域和类型检查规则。
    • 宏在预处理阶段展开,可能导致难以调试和维护的问题,而 inline 函数在编译阶段处理。
    #define ADD(a, b) ((a) + (b)) // 宏定义
    inline int add(int a, int b) { return a + b; } // inline 函数
    
  6. 递归函数

    • 递归函数通常不适合内联,因为每次递归调用都会导致函数体的重复展开,可能导致代码膨胀和性能问题。
    inline int factorial(int n) {return (n <= 1) ? 1 : n * factorial(n - 1); // 递归调用
    }
    
  7. 内联函数的调试

    • 内联函数可能会使调试变得复杂,因为内联展开后,函数调用栈信息可能不完整。
    • 使用调试器时,可能需要特别注意内联函数的展开情况。

2. inline 扩展

  1. 形参被对应实参取代
    inline 函数被调用时,函数的形参会被调用时传入的实参取代。这意味着在每个调用点,编译器会用实际的参数值替换函数体中的形参。

    inline int add(int a, int b) {return a + b;
    }int main() {int result = add(3, 4); // 这里会被扩展为 int result = 3 + 4;return 0;
    }
    
  2. 局部变量的引入
    inline 函数中定义的局部变量在每个调用点都会被引入。这可能会导致代码膨胀,特别是当 inline 函数被多次调用时。

    inline void printSum(int a, int b) {int sum = a + b;std::cout << "Sum: " << sum << std::endl;
    }int main() {printSum(3, 4); // 这里会被扩展为 { int sum = 3 + 4; std::cout << "Sum: " << sum << std::endl; }printSum(5, 6); // 这里会被扩展为 { int sum = 5 + 6; std::cout << "Sum: " << sum << std::endl; }return 0;
    }
    
  3. inline 失败的情形
    并不是所有的函数都适合被 inline。以下是一些 inline 可能失败的情形:

    • 函数体过于复杂或包含循环、递归等复杂控制结构。
    • 函数体过大,导致代码膨胀严重。
    • 函数包含静态变量或全局变量的修改。
    • 函数包含异常处理代码。
    inline void complexFunction() {for (int i = 0; i < 1000; ++i) {// 复杂的循环体}
    }int main() {complexFunction(); // 这里可能不会被内联,而是生成常规的函数调用return 0;
    }
    

总结

  • inline 函数的优缺点inline 函数可以提高执行效率,但可能导致代码膨胀。
  • inline 只是建议:编译器可能会忽略 inline 建议,生成常规的函数调用。
  • 定义和声明inline 函数通常在头文件中定义,以确保一致性。
  • 编译器优化:编译器可能自动内联某些函数,也可能忽略显式的 inline 建议。
  • 内联函数与宏的比较inline 函数比宏更安全,遵循 C++ 的作用域和类型检查规则。
  • 递归函数:递归函数通常不适合内联,可能导致代码膨胀和性能问题。
  • 内联函数的调试:内联函数可能使调试变得复杂,需要特别注意展开情况。
  • inline 扩展:包括形参被实参取代、局部变量的引入以及 inline 失败的情形。
文章来源:https://blog.csdn.net/qq_68194402/article/details/141114910
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ppmy.cn/news/1508716.html

相关文章

05_ Electron 自定义菜单、主进程与渲染进程通信

Electron 自定义菜单、主进程与渲染进程通信 一、定义顶部菜单二、Electron 自定义右键菜单1、使用 electron/remote 模块实现 三、 Electron 主进程和渲染进程通信场景1&#xff1a;渲染进程给主进程发送异步消息场景2&#xff1a;渲染进程给主进程发送异步消息&#xff0c;主…

链表 203.移除链表元素 虚拟头结点

普通方法 头节点要和其他节点分开考虑&#xff0c;因为头节点没有上一个节点&#xff0c;不能通过更改上一个结点的指针来达到删除节点的目的 所以要让下一个节点成为头节点&#xff0c;其余节点&#xff0c;通过更改上一个节点的next指针&#xff0c;指向next.next class S…

如何在 Windows 上设置 MacOS 云主机

在Windows上设置MacOS云主机实际上涉及在Windows环境中模拟或远程管理MacOS系统&#xff0c;因为直接在Windows上运行MacOS作为云主机的主操作系统是不可能的&#xff0c;因为MacOS是为苹果硬件设计的。不过&#xff0c;有几种方法可以实现类似的功能&#xff1a; 1. 使用虚拟机…

LVS理论知识

目录 1.描述以及工作原理 1.什么是LVS 2.LVS调度算法 1.静态调度算法 1.轮询RR 2.加权轮询WRR 3.目标地址hash---DH 4.源地址hash---SH 2.动态调度算法 1.LC最少连接 2.wlc加权最少连接 3.sed最少期望延迟 4.nq不排队调度算法 5.lblc基于本地最少连接 6.lnlcr带…

8.8 day bug

bug1 好家伙&#xff0c;最后一个t没看到&#xff0c;愣是学了一个小时原理和用法&#xff0c;都找不出问题在哪

教程:申请IP SSL证书

现在通过IP访问网站的方式也流行起来了&#xff0c;对于很多企业或是组织单位&#xff0c;申请域名不仅需要额外支付域名费用&#xff0c;还需要走备案流程&#xff0c;这对于很多单位来说显得麻烦了一些&#xff0c;所以利用IP直接进行网站访问也是必要的。 但是正常情况下IP…

达梦数据库 逻辑备份还原

达梦的逻辑备份还原 1.背景2.要求3.实验步骤3.1 相关术语3.2 dexp逻辑导出3.2.1 使用dexp工具3.2.2 dexp相关参数含义3.2.3 四种级别导出3.2.3.1 FULL3.2.3.2 OWNER3.2.3.3 SCHEMAS3.2.3.4 TABLES 3.2.4 使用范例3.2.4.1 环境准备3.2.4.2 dexp逻辑导出 3.3 dimp逻辑导入3.3.1 使…

一、软件工程概述

软件工程概述 1. 软件的概念和特点2. 软件危机的产生3. 软件工程的概念和发展过程4. 软件工程知识体系与职业道德 1. 软件的概念和特点 软件定义 软件程序数据文档。 软件生存周期 问题定义&#xff1a;要解决的问题是什么&#xff1f;可行性分析&#xff1a;对于上阶段所确定…