C++编程思想——内联

本文详细解释了内联函数和宏在程序中的作用及其调用过程。内联函数通过在编译时直接插入代码的方式提高了运行效率,适用于简单且频繁调用的方法;而宏则仅做简单的文本替换,不进行类型检查。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

内联

内联方法以关键字:inline来标志,其实我们也常常的看到内联的函数,比如在类头文件中定义方法。内联方法的调用与普通方法没有什么不同,但是其性能之间的差别非常的大。

下面我们从不同的角度来认识这个内联。

1)首先了解普通函数的调用过程:

将函数的参数入栈,栈指针指向第一个参数;

指令地址入栈,指令指针指向第一条指令;

此时在堆栈中将保存方法的栈指针、自变量指针及帧指针,这些都用来反应当前函数的上下文环境。

注:栈指针,跟踪堆栈的使用情况;指令指针,存放下一条执行指令的地址;而还有链接寄存器(LR)是存放指令指针的地址;帧指针以标识堆栈中两个区域的边界,一个区域含有记录方法中状态的寄存器,另一个区域是调用方法的自变量分配的内存)

函数返回过程:

将返回值存储到寄存器中;

将方法调用过程中保存的寄存器进行清理工作;

将帧指针寄存器以及自变量指针寄存器从堆栈中回复到其相应的位置;

修改栈指针寄存器使其指向方法的第一个参数压栈前的位置;

从堆栈中找到返回地址将其存入指令指针中,并返回到调用者调用的位置。

 

从上述过程中,我们看到一个普通的函数调用将需要很多的寄存器,指令,消耗的时钟周期增加;如果方法是返回一个对象时,将对象复制到调用方法为返回值留存的存储空间也是一个开销;还有的就是可能不能对跨越方法边界的代码进行优化。

 

2)使用内联调用函数的过程:

将内联方法的连续代码快复制到调用方法的调用点处;为变量分配内存;将内联方法的输入参数和返回值映射到调用方法的局部变量空间内。因此说内联是对运行时间进行的优化。适用情况1.函数较为的简单;2.一个函数不断的被重复调用。内联其实是以空间来换取时间。

 

3)避免内联

并不是任何时候都应该使用内联,当1.函数体内的代码较长时,因为内联会在调用出对代码进行展开;2.函数体内出现了循环。这两种情况下的内联会造成的代码的膨胀,尽管现在内存容量发展的很快,但是缓存问题和页面错误问题还是不能避免,当现有的缓存无法容纳所有需要的工作页面集合,命中率降低,频繁产生页面错误。 另外,滥用的内联程序尽管会执行较少的指令,但是会消耗较多的时间。

 

宏不是函数,它只是简单的替换,不会进行类型检查。预处理器复制宏代码代替函数调用,因此参数压栈,汇编语言的调用,等过程。而内联是在编译阶段插入代码。

 

C++编程思想,目 录 译者序 前言 第1章 对象的演化 1 1.1 基本概念 1 1.1.1 对象:特性+行为 1 1.1.2 继承:类型关系 1 1.1.3 多态性 2 1.1.4 操作概念:OOP程序像什么 3 1.2 为什么C++会成功 3 1.2.1 较好的C 3 1.2.2 采用渐进的学习方式 4 1.2.3 运行效率 4 1.2.4 系统更容易表达和理解 4 1.2.5 “库”使你事半功倍 4 1.2.6 错误处理 5 1.2.7 大程序设计 5 1.3 方法学介绍 5 1.3.1 复杂性 5 1.3.2 内部原则 6 1.3.3 外部原则 7 1.3.4 对象设计的五个阶段 9 1.3.5 方法承诺什么 10 1.3.6 方法应当提供什么 10 1.4 起草:最小的方法 12 1.4.1 前提 13 1.4.2 高概念 14 1.4.3 论述(treatment) 14 1.4.4 结构化 14 1.4.5 开发 16 1.4.6 重写 17 1.4.7 逻辑 17 1.5 其他方法 17 1.5.1 Booch 18 1.5.2 责任驱动的设计(RDD) 19 1.5.3 对象建模技术(OMT) 19 1.6 为向OOP转变而采取的策略 19 1.6.1 逐步进入OOP 19 1.6.2 管理障碍 20 1.7 小结 21 第2章 数据抽象 22 2.1 声明与定义 22 2.2 一个袖珍C库 23 2.3 放在一起:项目创建工具 29 2.4 什么是非正常 29 2.5 基本对象 30 2.6 什么是对象 34 2.7 抽象数据类型 35 2.8 对象细节 35 2.9 头文件形式 36 2.10 嵌套结构 37 2.11 小结 41 2.12 练习 41 第3章 隐藏实现 42 3.1 设置限制 42 3.2 C++的存取控制 42 3.3 友元 44 3.3.1 嵌套友元 45 3.3.2 它是纯的吗 48 3.4 对象布局 48 3.5 类 48 3.5.1 用存取控制来修改stash 50 3.5.2 用存取控制来修改stack 51 3.6 句柄类(handle classes) 51 3.6.1 可见的实现部分 51 3.6.2 减少重复编译 52 3.7 小结 54 3.8 练习 54 第4章 初始化与清除 55 4.1 用构造函数确保初始化 55 4.2 用析构函数确保清除 56 4.3 清除定义块 58 4.3.1 for循环 59 4.3.2 空间分配 60 4.4 含有构造函数和析构函数的stash 61 4.5 含有构造函数和析构函数的stack 63 4.6 集合初始化 65 4.7 缺省构造函数 67 4.8 小结 68 4.9 练习 68 第5章 函数重载与缺省参数 69 5.1 范围分解 69 5.1.1 用返回值重载 70 5.1.2 安全类型连接 70 5.2 重载的例子 71 5.3 缺省参数 74 5.4 小结 81 5.5 练习 82 第6章 输入输出流介绍 83 6.1 为什么要用输入输出流 83 6.2 解决输入输出流问题 86 6.2.1 预先了解操作符重载 86 6.2.2 插入符与提取符 87 6.2.3 通常用法 88 6.2.4 面向行的输入 90 6.3 文件输入输出流 91 6.4 输入输出流缓冲 93 6.5 在输入输出流中查找 94 6.6 strstreams 96 6.6.1 为用户分配的存储 96 6.6.2 自动存储分配 98 6.7 输出流格式化 100 6.7.1 内部格式化数据 101 6.7.2 例子 102 6.8 格式化操纵算子 106 6.9 建立操纵算子 108 6.10 输入输出流实例 111 6.10.1 代码生成 111 6.10.2 一个简单的数据记录 117 6.11 小结 123 6.12 练习 123 第7章 常量 124 7.1 值替代 124 7.1.1 头文件里的const 124 7.1.2 const的安全性 125 7.1.3 集合 126 7.1.4 与C语言的区别 126 7.2 指针 127 7.2.1 指向const的指针 127 7.2.2 const指针 127 7.2.3 赋值和类型检查 128 7.3 函数参数和返回值 128 7.3.1 传递const值 128 7.3.2 返回const值 129 7.3.3 传递和返回地址 131 7.4 类 133 7.4.1 类里的const和enum 133 7.4.2 编译期间类里的常量 134 7.4.3 const对象和成员函数 136 7.4.4 只读存储能力 139 7.5 可变的(volatile) 140 7.6 小结 141 7.7 练习 141 第8章 内联函数 142 8.1 预处理器的缺陷 142 8.2 内联函数 144 8.2.1 类内部的内联函数 145 8.2.2 存取函数 146 8.3 内联函数和编译器 150 8.3.1 局限性 150 8.3.2 赋值顺序 150 8.3.3 在构造函数和析构函数里隐藏行为 151 8.4 减少混乱 152 8.5 预处理器的特点 153 8.6 改进的错误检查 154 8.7 小结 155 8.8 练习 155 第9章 命名控制 157 9.1 来自C语言中的静态成员 157 9.1.1 函数内部的静态变量 157 9.1.2 控制连接 160 9.1.3 其他的存储类型指定符 161 9.2 名字空间 161 9.2.1 产生一个名字空间 162 9.2.2 使用名字空间 163 9.3 C++中的静态成员 166 9.3.1 定义静态数据成员的存储 166 9.3.2 嵌套类和局部类 168 9.3.3 静态成员函数 169 9.4 静态初始化的依赖因素 171 9.5 转换连接指定 174 9.6 小结 174 9.7 练习 174 第10章 引用和拷贝构造函数 176 10.1 C++中的指针 176 10.2 C++中的引用 176 10.2.1 函数中的引用 177 10.2.2 参数传递准则 178 10.3 拷贝构造函数 179 10.3.1 传值方式传递和返回 179 10.3.2 拷贝构造函数 182 10.3.3 缺省拷贝构造函数 187 10.3.4 拷贝构造函数方法的选择 188 10.4 指向成员的指针
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值