C++ 函数 内联函数
内联函数的功能和预处理宏的功能相似,在介绍内联函数之前,先介绍一下预处理宏。宏是简单字符替换,最常见的用法:定义了一个代表某个值的全局符号、定义可调用带参数的宏。作为一种约定,习惯上总是用大写字母来定义宏,宏还可以替代字符常量。我们会经常定义一些宏,如:
#define ADD(a,b) a+b
那为什么需要使用宏呢?因为调用函数需要一定的时间和空间开销。
执行到函数调用指令时,程序将在函数调用后立即存储该指令的内存地址,并将函数参数复制到堆栈(为此保留的内存块),跳到标记函数起点的内存单元,执行函数代码(也许还需将返回值放入寄存器中),然后跳回到地址被保存的指令处(这与阅读文章时停下来看脚注,并在阅读完脚注后返回到以前阅读的地方类似)。来回跳跃并记录跳跃位置意味着以前使用函数时,需要一定的开销。
而宏仅仅是在预处理的地方把代码展开,不需要额外的时间空间方面的开销。
宏也有很多的不尽人意的地方,所以c++中用内联函数来代替宏。
(1)宏不能访问对象的私有成员。类的私有成员只能通过类的成员函数或友元函数来访问,
(2)宏不进行类型检查。例如上面定义的ADD宏,要注意传入实参的类型,如果你传入的参数不是char, int, float, double,而是其他类型,可能就会出错。
(3)宏的定义很容易产生二义性。
#define MULTI(x) (x*x)
我们用一个数字去调用它,MULTI(10),这样看上去没有什么错误,结果返回100,是正确的;但是如果我们用MULTI(10+10)去调用的话,我们期望的结果是400,而宏的调用结果是(10+10*10+10),结果是120,这显然不是我们要得到的结果。为避免这种错误,可给宏的参数都加上括号。
#define MULTI(x) ((x)*(x))
这样可以确保MULTI(10+10)不会出错,但是若使用MULTI(a++)调用它,他们本意是希望得到(a+1)*(a+1)的结果,但是宏的展开结果是:(a++)*(a++),如果a的值是2,我们得到的结果是2*3=6。而我们期望的结果是3*3=9。
内联函数的方法很简单,只需在函数首行的左端加一个关键字inline即可 。
在编译时将所调用函数的代码直接嵌入到主调函数中,而不是将流程转出去,这种嵌入到主调函数中的函数成为内联函数。
#include <iostream>
using namespace std;
inline int max(int,int, int); //声明函数,注意左端有inline
int main( )
{
int i=,j=,k=,m;
m=max(i,j,k);
cout<<″max=″<<m<<endl;
return ;
}
inline int max(int a,int b,int c) //定义max为内置函数
{
if(b>a) a=b; //求a,b,c中的最大者
if(c>a) a=c;
return a;
}
由于在定义函数时指定它为内置函数,因此编译系统在遇到函数调用“max(i,j,k)”时,就用max函数体的代码代替“max(i,j,k)”,同时将实参代替形参。
这样,程序代码“m=max(i,j,k);”就被置换成:
if (j>i) i=j;
if (k>i) i=k;
m=i;
内联函数必须是和函数体在一起,才有效。如下风格的函数不能成为内联函数:
inline void Fun(int x, int y); // inline仅与函数声明放在一起
void Fun (int x, int y) {…}
而如下风格的函数Fun则成为内联函数:
void Fun(int x, int y);
inline void Fun(int x, int y) {…} // inline与函数定义体放在一起
因此,inline是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。
内联函数和宏的区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。内联函数与带参数的宏定义进行下比较,它们的代码效率是一样,但是内联欢函数要优于宏定义,因为内联函数遵循的类型和作用域规则,它与一般函数更相近,在一些编译器中,一旦关联上内联扩展,将与一般函数一样进行调用,比较方便。
另外,宏定义在使用时只是简单的文本替换,并没有做严格的参数检查,也就不能享受C++编译器严格类型检查的好处,另外它的返回值也不能被强制转换为可转换的合适的类型,这样,它的使用就存在着一系列的隐患和局限性。C++的inline的提出就是为了完全取代宏定义,因为inline函数取消了宏定义的缺点,又很好地继承了宏定义的优点
内联函数是以代码膨胀(复制)为代价,这省去了函数调用的开销,从而提高函数的执行效率。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜使用内联:
(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
(2)内联函数中不能包括复杂的控制语句,如循环语句和switch语句。如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。
同时要注意inline关键字只是表示一个请求,编译器并不会一定将inline修饰的函数作为内联,有的没有被inline修饰的函数在一些编译器中也可能被编译为内联。
C++ 函数 内联函数的更多相关文章
- [ 随手记 1 ] C/C++宏,普通函数,内联函数
函数定义 C 语言中的函数定义的一般形式如下: return_type function_name( parameter list ) { body of the function } 在 C 语言中 ...
- 宏 函数 内联函数inline
带参宏有时候可以代替函数作用:优点直接替代,省去函数调用过程的开销:但缺点也是很明显:容易出错,系统不做检查非常容易出错. 改进方案:内联函数:既有带参宏的直接替代(拷贝)的优点,又有系统检查的优点. ...
- C++内联函数
在C语言中,我们使用宏定义函数这种借助编译器的优化技术来减少程序的执行时间,那么在C++中有没有相同的技术或者更好的实现方法呢?答案是有的,那就是内联函数.内联函数作为编译器优化手段的一种技术,在降低 ...
- 为什么内联函数,构造函数,静态成员函数不能为virtual函数
http://blog.csdn.net/freeboy1015/article/details/7635012 为什么内联函数,构造函数,静态成员函数不能为virtual函数? 1> 内联函数 ...
- C/C++ 内联函数
内联函数具备一般函数的性质,但是不需要调用,而是在编译阶段,会用函数体替换函数名被调用的地方.可以节省调用时间(进出栈.保存上下文). 在编译层面和宏的作用相同.内联函数的展开在编译阶段,宏展开在预处 ...
- 特殊用途语言特性——默认参数、内联函数和constexptr函数
1 默认实参 某些函数有这样一些参数,在函数的很多次调用中它们都被赋予一个相同的值,此时,我们把这个反复出现的值称为函数的默认实参.调用含有默认实参的函数时,可以包含该实参,也可以省略该实参. 我们可 ...
- inline内联函数
demo //带参数的宏 #define MYFUNC(a, b) ((a) < (b) ? (a) : (b)) inline int myfunc(int a, int b) { retur ...
- C++ : 内联函数和引用变量
一.内联函数 内联函数和普通函数的使用方法没有本质区别,我们来看一个例子,下面展示了内联函数的使用方法: #include <iostream> using namespace std; ...
- c++中函数的参数传递,内联函数和默认实参的理解
1.参数传递 1)函数调用时,c++中有三种传递方法:值传递.指针传递.引用传递. 给函数传递参数,遵循变量初始化规则.非引用类型的形参一相应的实参的副本初始化.对(非引用)形参的任何修改仅作用域局部 ...
随机推荐
- 关于VS2010 C#使用DirectX的问题[英]
转载的,就不翻译了…微软把精力放到xna去了.所以推荐大家用XNA,如果非要用托管的DirectX也可以,只不过版本一直是2006年的了. 具体方法: 安装SDK之后 他默认的位置在C:\WINDOW ...
- 递归计算一个目录的大小【os.wallk()】
os.walk(): os.walk()可以得到一个三元tupple(dirpath, dirnames, filenames),其中第一个为起始路径,第二个为起始路径下的文件夹,第三个是起始路径下的 ...
- UITextView 和 UITextField限制字符数和表情符号
UITextField限制字符数 - (BOOL)textField:(UITextField *)textField shouldChangeCharactersInRange:(NSRange)r ...
- ZOJ 3983 Crusaders Quest(思维题)
C - Crusaders Quest Time Limit:1000MS Memory Limit:65536KB 64bit IO Format:%lld & %llu S ...
- [图解tensorflow源码] Simple Placer节点布放算法
- Java面向对象之多态(来源于身边的案例)
2019年1月3日 星期四 Java面向对象之多态(来源于身边的案例) 1. 为什么要用多态? 1.1 多态是面向对象的三大特性之一 1.2 多态是基于接口设计的模型 1.3 多态具有横向扩展特性 1 ...
- js动态生成水印
原理:通过动态生成canvas然后转为base64格式 代码Demo export const waterMark = (text) =>{ let _wm = document.createE ...
- c++类模板之友元函数
前言:自从开始学模板了后,小编在练习的过程中.常常一编译之后出现几十个错误,而且还是那种看都看不懂那种(此刻只想一句MMP).于是写了便写了类模板友元函数的用法这篇博客.来记录一下自己的学习. 普通友 ...
- CentOS7.3安装mysql数据库
Mysql数据库安装 1.环境 操作系统:CentOS 7.3 软件:MySQL 5.7 下载链接:https://cdn.mysql.com//Downloads/MySQL-5.7/mysql-5 ...
- Linux磁盘与文件系统管理(一)
fdisk 常用的磁盘分区工具,受mbr分区表的限制,只能给小于2TB的磁盘划分分区,如果使用fdisk对大于2TB的磁盘进行分区,虽然可以分区,但只能识别2T的空间,一般使用parted分区工具 - ...