内联函数
C++中推荐使用内联函数替代宏代码片段
C++中使用inline关键字声明内联函数
inline int func(int a ,int b)
{
return a < b ? a : b;
}
内联函数声明时inline关键字必须和函数定义结合在一起,不是和函数声明结合在一起,否则编译器会直接忽略内联请求
C++编译器可以将一个函数进行内联编译
被C++编译器内联编译的函数叫做内联函数
内联函数在最终生成的代码中时没有定义的
C++编译器直接将函数体插入函数调用的地方
int main()
{
int c = func(1,2); ==> int c = 1 < 2 ? 1 : 2;
......
}
内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)
C++编译器不一定准许函数的内联请求!编译器不准许则按普通函数处理
内联函数是一种特殊的函数,具有普通函数的特性(参数检查,返回类型等)
内联函数是对编译器的一种请求,因此编译器可能拒绝这种请求
内联函数由编译器处理,直接将编译后的函数体插入调用的地方
.宏代码片段由预处理器处理,进行简单的文本替换,没有任何编译过程
内联函数没有宏的副作用,较宏更安全
例:
#define FUNC(a,b) ((a) < (b) ? (a) : (b))
inline int func(int a,int b)
{
return a < b ? a : b;
}
int main()
{
int a = 1;
int b = 3;
int c = func(++a,b);//采用内联函数输出2,3,2
//int c =FUNC(++a,b);//采用宏输出3,3,3
printf("a = %d\n",a);
printf("b = %d\n",b);
printf("c = %d\n",c);
......
}
现代C++编译器能够进行编译优化,因此一些函数即使没有inline声明,也可能被编译器内联编译
另外一些现代C++编译器提供了扩展语法,能够对函数进行强制内联
如:g++中的__attribute__((always_inline))属性
C++中内联编译的限制:
.不能存在任何形式的循环语句
.不能存在过多的条件判断语句
.函数体不能过于庞大
.不能对函数进行取址操作
.函数内联声明必须在调用语句之前
编译器罪域内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。因此,当函数体的执行开销远大于压栈,跳转和返回所用的开销时,那么内联将无意义。
C++中内联函数的实现机制
C++编译到func函数是发现有内联声明,就是说程序请求将func函数做成内联函数,C++编译器这时就智能的去看下他的函数体,发现这个函数还是比较简单,符合做成内联函数的要求,于是C++编译器就将这个函数放到他的符号表里面,然后继续编译,编译到int r=func(1,2);这条语句时,发现func在符号表里面能找到,于是编译器就先对这个func调用里面的两个参数进行类型检查看看书否匹配,发现匹配然后编译器就直接到符号表里将func函数的函数体拿出来直接放到func(1,2)这个地方,所以说一个函数如果成功的进行内联编译后,在他最后生成的汇编代码里根本找不到函数调用跟函数定义的代码。
函数默认参数
C++中可以在函数声明时为参数提供一个默认值,当函数调用时没有指定这个参数的值,编译器会自动用默认值代替
例:
int mul(int x = 3); //声明时指定默认参数值
int main(int argc, char *argv[])
{
printf("mul(2) = %d\n",mul(2)); //输出4
printf("mul(-2) = %d\n",mul(-2)); //输出4
printf("mul() = %d\n",mul()); //输出9
......
}
int mul(int x) //定义时不再指定
{
return x * x;
}
函数默认参数的规则:
.只有参数列表后面部分的参数才可以提供默认参数值
.一旦在一个函数调用中开始使用默认参数值,那么这个参数后的所有参数都必须使用默认参数值
例:
int add(int a, int b = 0, int c = 0)
{
return a + b + c;
}
int main(int argc, char *argv[])
{
printf("add(2) = %d\n",add(2)); //从第二个参数开始使用默认参数值,那么第三个参数也必须使用默认参数值,输出2
printf("add(1,2) = %d\n",add(1,2));//从第二个参数开始使用默认参数值,输出3
printf("add(1,2,3) = %d\n",add(1,2,3));//未使用默认参数值,输出6
......
}
函数占位参数
在C++中可以为函数提供占位参数
.占位参数只有参数类型声明,而没有参数名声明
.一般情况下,在函数体内部无法使用占位参数
例:
int func(int a, int b, int) //第三个参数没有名字是占位参数,因为没有名字所以在函数体内无法使用,占位参数本质上也是一个参数,调用这个函数的时候必须提供3个实参
{
return a + b;
}
int main(int argc, char *argv[])
{
printf("func(1,2,3) = %d\n",func(1,2,3)); //输出3,func必须提供3个参数,否则(如func(1,2))会报错
......
}
C++支持这样的函数占位参数有什么意义?
可以将占位参数与默认参数结合起来使用
意义:
1.为以后程序的扩展留下线索;
int func(int a, int b, int = 0)
工程中看到这样的写法,就知道原来的作者这么写是为了告诉后来的维护者这个函数是有可能改动的,他的功能可能没有完成,但是暂时在项目里又够用,也许未来哪一天这个函数就需要被改写或者被重写
2.兼容C语言程序中可能出现的不规范写法
例:
test.c程序
int func()
{
return 1;
}
int main(int argc, char *argv[])
{
printf("func() = %d\n",func());
printf("func(1) = %d\n",func(1));
......
} //在C编译器中正确编译通过
test.cpp程序
int func()
{
return 1;
}
int main(int argc, char *argv[])
{
printf("func() = %d\n",func());
printf("func(1) = %d\n",func(1)); //C++编译器此处报错
......
}
test.cpp程序
int func(int = 0) //也可提醒程序员此处有占位参数,又提供了一个默认值,证明这个函数是有问题的,日后有时间修改这个C程序移植到C++程序中不规范的问题
{
return 1;
}
int main(int argc, char *argv[])
{
printf("func() = %d\n",func());
printf("func(1) = %d\n",func(1));
......
} //C++编译正确通过
小结
C++中可以通过inline声明内联函数
内联函数在编译时直接将函数体插入函数调用的地方
inline只是一种请求,编译器不一定允许这种请求
内联函数省去了普通函数调用时压栈,跳转,返回的开销
C++中在声明函数时指定参数的默认值
C++可以声明占位符参数,占位符参数一般用于程序扩展和对C代码的兼容