C++中的函数新特性:先来速成新特性1. 内联函数,说到内联函数,不得不提和它类似的一个东西先。
#define F(x) ((x)*(x))
没错,就是宏定义。
在程序编译过程中,F(x)会自动替换成((x)*(x))
从而在程序中F(x)就像一个函数一样
C++中当然也有宏定义
不过有一个新特性
能够得到与此类似的实现
inline int F(int x)
{
return x*x;
}
和普通函数的定义几乎一模一样,不过就是在声明语句前面加了一个词
inline
作用:和宏定义类似,在函数调用的地方,
编译器将会使用相应的函数代码替换函数的调用
也就是在编译阶段就替换代码,
实际运行的时候已经没有函数调用了
这一点是和宏定义类似的
2. 函数参数默认值
在C++的函数中,我们可以在声明的时候添加默认值
语法简介
先 放码过来 展示下语法
如下面
这是函数原型:
int function(int a = 2)
这是函数定义:
int function(int a)
{
return a*a;
}
然后在调用函数的时候,我们可以这样
function(6);
//返回36
function(4);
//返回16
function();
//返回4
嗯??第三条语句并没有传递参数呀?
在没有传递参数的时候
函数会自动采用在声明时使用的默认值
3. 函数重载
C++中,是允许同名函数的存在的
在调用函数的时候,
会根据函数的形参列表选择其中一个执行
所谓函数重载便是如此
例:我们可以定义这样一组函数
void print(int a);
void print(int a,int b);
void print(double a);
void print(int a, double b);
void print(doubla a, int b);
然后我们在调用函数的时候
print (3);// 调用第一个
print (3,4); //调用第二个
print (3.0); //调用第三个
print (3,2.0); //调用第四个
print (2.0, 2); //调用第五个
这几个调用都会调用不同的函数
4. 函数模板
如果我要打一个交换两个int变量的函数
我可以这样
void swap(int & a, int & b)
{
int temp;
temp = a;
a = b;
b = temp;
}
如果我还要打一个交换double变量的呢?
如果我还要交换long变量呢?
我是不是都要打一个swap函数!!
好烦!!
函数模板,就是为了解决你这样的烦恼而来
且看下面
template
void swap(T & a, T & b)
{
T temp;
temp = a;
a = b;
b = temp;
}
关键字:template(模板)
关键字:typename
第一行,指出这是一个函数模板
一个可替代的变量类型,我们暂且命名为T
然后后面函数的定义
就用T来代替具体的类型
之后我们的函数调用
就可以用这一个swap函数,去交换int变量
去交换double变量
实际上函数模板就是在编译的时候
将T换成所需要的类型
就像 typedef T int ;一样
下面是一些比较细的注意事项
最好边看边打码边实践
1. 内联函数
为什么我们需要内联函数
1. 为了效率
函数的调用原理
函数调用时
需要程序在运行时跳到调用函数所在地址
然后执行函数后,跳回上一级并且返回值
在这一跳一跳的过程
需要做很多事情
宏定义和内联函数
都通过直接替换代码的做法
避免了常规的函数调用
节省了计算资源和存储空间
2. 为了避免宏定义的弊端
#define F(x) ((x)*(x))
就如同这条宏定义
为什么要加这么多括号呢?
(希望能够自己思考一下)
如果不加
当我们使用F(x+1)
宏定义会替换成 x+1 * x+1
明显不能算出我们所期待的x+1 的平方
再如,如果在含有这条宏定义的程序中
我们这样
F(c++);
又会发生什么呢?
替换后会变成
c++ * c++
结果不好确定
而且c还被加了两遍
一定不会是自己所期望自己的结果
返回c的平方后c++;
这些种种弊端,内联函数都不存在
只管当普通函数用就好啦
使用内联函数的注意事项
被调用多次且短小的程序适合作为内联函数 只被调用一两次,或者函数定义代码比较长,不适合用内联函数 !! 内联函数不能递归
2. 函数参数默认值
两个注意事项
从右向左添加默认值 默认值只加在函数原型,函数定义中不用加参数默认值
下面我解释一下上面的两句话
1. 从右向左添加默认值
假设有这样的一个函数原型
int function (int a, int b = 3, int c )
我这样调用
function(2,4);
这是想做什么呢?
是因为b有了默认值,想只给a和c赋值吗?
但是
实参按从左向右的顺序依次赋给对应的形参
也就是说
2 会赋给 a , 4 会赋给 b,
不会跳过默认值
不过,我们可以这样修改函数原型
int function (int a, int c , int b = 3)
这样就没有问题了
所以,默认值从右往左添加
2. 只有函数原型需要加默认值
这个注意事项相信大家再看会该节开头的例子就懂了
这是函数原型:
int function(int a = 2)
这是函数定义:
int function(int a)
{
return a*a;
}
3. 函数重载
函数特征标
先明白一个概念:函数的特征标
函数特征标
对于相同名字的函数
函数的特征标由函数定义的形参列表决定
例:
int function (int , int );
int function (double );
第一个函数的形参列表为(int, int)
第二个函数的形参列表为(double)
所以虽然相同名字,这是两个不同的函数
成功重载的条件
const可以重载吗
void print(int a);
void print (const int a);
这个是不可以的,
void print (int & a);
void print (const int & a);
这个是可以的
void print (int * a);
void print (const int * a);
这个也是可以的
我其实很想解释解释为什么可以为什么不可以的
然后发现我不会解释
不过在DEV上亲测过
引用变量能重载吗
void print (int a);
void print (int & a);
这两个是无法重载的
二义性与类型转换
假设这里定义了这样的函数
void print (int a, double = 3);
void print (int b);
这两个当然是不同的函数,当然可以重载
可是,如果我这样调用函数
print(3);
对于第一个版本,当然可以使用,因为第二个参数默认为3,可以省略
对于第二个版本,毫无疑问可以使用
这下子怎么办????
编译器就会报错
这就叫做具有二义性
二义性是指
访问同名成员的不确定性
包括访问函数,访问类成员。。。
在类型转换中,同样可能会有二义性
例子:这里定义了这几个函数
void print (int a);
void print (double a);
然后
unsigned int u = 3;
print(u);
会发生什么?
这里没有一个函数与u匹配,
因此u会尝试类型转换从而匹配
u既可以转为int类型也可以转为double类型
出现二义性
编译器无法确定
报错