C++中的函数玩法

内联函数(inline)

        函数调用是有时间和空间开销的。程序在执行一个函数之前需要做一些准备工作,要将实参、局部变量、返回地址以及若干寄存器都压入栈中,然后才能执行函数体中的代码;函数体中的代码执行完毕后还要清理现场,将之前压入栈中的数据都出栈,才能接着执行函数调用位置以后的代码。
        如果函数体代码比较多,需要较长的执行时间,那么函数调用机制占用的时间可以忽略; 如果函数只有一两条语句,那么大部分的时间都会花费在函数调用机制上,这种时间开销就就不容忽视。
        为了消除函数调用的时空开销,C++ 提供一种提高效率的方法,即在编译时将函数调用处用函数体替换,类似于 C 语言中的宏展开。 这种在函数调用处直接嵌入函数体的函数称为内联 函数( Inline Function ),又称内嵌函数或者内置函数。指定内联函数的方法很简单,只需要在函数定义处增加 inline 关键字。请看下面的例子:
#include <iostream>
using namespace std;
//内联函数,交换两个数的值
inline void swap(int *a, int *b)
{
    int temp;
    temp = *a;
    *a = *b;
    *b = temp;
}
int main()
{
    int m, n;
    cin>>m>>n;
    cout<<m<<", "<<n<<endl;
    swap(&m, &n);
    cout<<m<<", "<<n<<endl;
    return 0;
}
        当编译器遇到函数调用 swap(&m, &n) 时,会用 swap() 函数的代码替换 swap(&m, &n) ,同时用实参代替形参,以下为替换后的结果:
int main()
{
    int m, n;
    cin>>m>>n;
    cout<<m<<", "<<n<<endl;
    {
        int temp;
        temp = *(&m);
        *(&m) = *(&n);
        *(&n) = temp;
    }
    cout<<m<<", "<<n<<endl;
    return 0;
}
        编译器可能会将 *(&m) *(&n) 分别优化为 m n
        当函数比较复杂时,函数调用的时空开销可以忽略,大部分的 CPU 时间都会花费在执行函数体代码上,所以我们一般是将非常短小的函数声明为内联函数。由于内联函数比较短小,我们通常的做法是省略函数原型,将整个函数定义(包括函数头和函数体)放在本应该提供函数原型的地方,也就是声明时定义。
        注意:要在函数定义处添加 inline 关键字,在函数声明处添加 inline 关键字虽然没有 错,但这种做法是无效的,编译器会忽略函数声明处的 inline 关键字。
        使用内联函数的缺点也是非常明显的,编译后的程序会存在多份相同的函数拷贝,如果被声明为内联函数的函数体非常大,那么编译后的程序体积也将会变得很大,所以再次强调,一 般只将那些短小的、频繁调用的函数声明为内联函数。

         对函数作 inline 声明只是程序员对编译器提出的一个建议,而不是强制性的,并非一经指定为 inline 编译器就必须这样做。编译器有自己的判断能力,它会根据具体情况决定是否这样做。

内联函数和宏定义的区别

        使用宏代码最大的缺点是容易出错,预处理在拷贝宏代码时常常产生意向不到的边际效应。
例如:
#define MAX(a,b) (a)>(b)?(a):(b)
//语句为
result = MAX(i,j)+2;
//被预处理器扩展为
result = (i)>(j)?(i):(j)+2;

由于运算符"+"比运算符"?:"的优先级高,所以上述语句并不等价于

result = ((i)>(j)?(i):(j))+2;

如果把宏代码改写为:

#define MAX( a , b ) ((a) > (b) ? (a) : (b))
则能解决优先级的问题。但是会引发另一个问题
result = MAX (i++,j);
// 被预处理器扩展为
result = (i++)>(j)?(i++):(j) // 在同一个表达式中 i 被两次求值。
        宏的另一个缺点是不可调试。内联机制既具备宏代码的效率,又增加了安全性,而且可以自由操作的类的数据成员,所以应该尽量使用内联函数来取代宏代码。

默认参数

在 C++中,可以为函数的形参指定默认的参数值,例如:

int sun(int a,int b = 3,int c = 7);
int Opendev(int cfg,char *name = "1.txt");
        如上,这样我们在调用函数的时候,就可以只选择传递没有默认参数的形参,例如,在调用上面 sun 的时候,我就可以
sun(3);
//或
sun(3,4);
//或
sun(3,4,5);

注意事项

在声明默认参数时,必须是从右向左声明,也就是说,没有默认参数的形参应该全部在前面,下面这些就是错误示例:
int fun ( int a , int b = 1 , int c );
void run ( char* name = "2.bmp" , int cfg );

占位参数

        在 c++中提供了占位参数用于程序扩展和对 C 语言的兼容,什么是占位参数呢,就是只声明数据类型而没有形参名称,一般在函数体内部无法使用该参数,例如:
int func(int a, int b, int)
{
    return a+b;
}
int main(int argc, char *argv[])
{
    printf("fun(1,2,3) = %d\n", func(1,2,3));
    printf("Press enter to contiue ...");
    return 0;
}
一般占位参数配合默认参数一起使用,例如:
int func(int a, int b, int = 0)
{
    return a+b;
}
int main(int argc, char *argv[])
{
    printf("func(1,2) = %d\n", func(1,2));
    printf("Press enter to continue ...");
    return 0;
}
这样在调用的时候,你就可以把上面的 func 函数当作一个两参数函数使用。

函数重载

        c++的三大特性,封装,继承,多态,其中多态又分为编译时多态和运行时多态,本次要谈的函数重载便是 C++编译时多态的一种体现。
        前面章节我们讲过,在 C++中,函数会被经过换名处理,新的名字是受返回值,函数名,形参决定,所以只要是参数类型不同,参数个数不同,参数的顺序就可以定义相同的函数名。
例如:
#include <iostream>
using namespace std;
int sun(int a,int b);
int sun(int a,int b,int c);
int main()
{
    cout<<sun(1,2)<<endl;
    return 0;
}
int sun(int a,int b)
{
    return a+b;
}
int sun(int a,int b,int c)
{
    return a+b+c;
}
在编译的时候,系统会根据你传递的实参个数或类型来匹配对应的函数,这就叫函数重载。

注意事项

C++函数重载必须要满足的条件是:
1.函数名相同。
2.参数列表相同。
当函数重载遇上默认参数的时候,情况例如下面所示:
int sun ( int a , int b );
int sun ( int a , int b , int c = 3 );
        因为带默认参数的函数,传递参数时传递的个数是可变的,那么当遇上上面情况时,如果调用 sun(2,3)就会报错,会产生模糊的调用,而调用 sun(2,3,4)则可以正确的重载到 sun(int a,int b,int c =3)这个函数。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

九月丫

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值