C++基础知识(五)--函数重载--缺省参数--内联函数--内联函数与宏与一般函数的区别
一、函数重载--用相同的函数名定义一组功能相同或类似的函数,程序的可读性增强
1函数重载:在实际的开发中需要定义几个功能类似而参数类型不同的函数那么这样的几个函数可以使用相同的函数名,这就是函数的重载。简言之就是一个函数名可以有多种用途。
2.函数重载提高了程序的可读性,函数参数列表的不同包括:参数的个数不同,类型不同,或顺序不同,仅仅参数名称不同是不可以的,函数的返回值也不能作为重载的依据。
3.函数重载的规则:
- 函数名称必须相同
- 参数列表必须不同(个数不同,类型不同,参数排列顺序不同等)
- 函数的返回类型可以相同也可以不同
- 仅仅返回类型不同不足以成为函数重载的依据
4.C++是如何实现函数重载的
1.C++代码在编译的时候会根据参数列表进行重命名,例如void swap(int a,int b)会被重命名为_swap_int_int,,,void swap(float x,float y)会被重命名为_swap_float_float。当发生函数调用时,编译器会根据传入的实参去逐个匹配,以选择对应的函数,如果匹配失败,编译器就会报错,这叫做重载决议。
2.函数的重载仅仅是语法上的不同,本质上还是不同的函数,占用的内存不同,函数的入口地址也不同。
1 int sum(int a,int b) 2 double sum(double a,double b) 3 float sum(float a,float b,float c)
二、 C++函数缺省参数
1.默认参数:在定义函数时为形参指定默认值(缺省值),这样函数在调用时,对于默认参数可以给出实参值也可以不给出实参值。如果给出实参值,将实参传递给形参进行调用,如果不给出实参,则按默认值进行调用。
2.默认参数的函数调用:默认实参并不一定是常量表达式,可以是任意表达式甚至可以通过函数调用给出,如果默认实参是任意表达式,则函数每次调用时该表达式被重新求值。但表达式必须有意义。
3.默认的参数可以有多个,但所有默认参数必须放在参数表的右侧,即先定义所有的非默认参数,再定义默认参数。这是因为在函数调用时,参数自左向右逐个匹配,当实参与形参个数不一致时只有这样才不会产生二义性。
4.在同一作用域内,一个参数只能被指定一次默认值,不可以在声明和定义中同时指定默认值,即使默认值一样也不行。习惯上,缺省参数在公共头文件包含的函数声明中指定,否则缺省函数只能用于包含该函数定义的文件中的函数调用。
5.在调用具有默认参数的函数时,若某个实参默认,其右边的所有实参都应该默认
int fun(int i1=1,int i2=2,int i3=3); //调用函数 fun();//正确全为默认值 fun(3);//正确 i1=3,i2=2,i3=3 fun(3,,4);//正确i1=3,i2=4,i3=3 fun(2,3,4);//正确 i1=2,i2=3,i3=4 fun(,6,7);错误i1为默认,i2,i3都不是默认
三、C++内联函数------------ --本质是节省时间消耗空间
1.内联函数的引入
- 当程序执行函数调用时系统需要建立栈空间,保护现场,传递参数以及控制程序执行的转移等等,这些工作需要系统时间和空间的开销
- 当函数功能简单,使用频率很高时为了提高效率,直接将函数的代码嵌入到程序中,,这个办法有缺点,一是相同代码重复书写,而是程序可读性没有使用函数好
- 为了协调好效率与可读性之间的矛盾,C++引入了内联函数
2.内联函数的定义
在函数声明前加inline
在函数定义前加inline
3.内联函数的调用
- 内联函数的调用机制与一般函数不同,编译器在编译过程中遇到inline时,为该函数内建一段代码,然后在每次调用时直接将该代码嵌入到调用函数中,从而将函数调用方式变为顺序执行方式,这一过程成为内联函数的的扩展或内联。因为inline指示符对编译器而言只是一个建议,编译器也可以选择忽略该建议。
4.只有当函数只有10行甚至更少时才将其定义为内联函数。内联是以代码膨胀(复制)为代价的,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间相比较于函数调用的的开销较大,那么效率就不明显了,而且每一处内联函数的调用都要复制代码,使程序的总代码量增加,消耗更多的内存空间。
- 内联函数的使用规则,inline函数何时不被展开
- 含有递归调用的函数不能设置为内联函数:因为程序无法在每次编译时知道要递归的次数,所以不能进行代码替换
- 使用了循环语句与开关语句(switch)的函数不能设置为内联函数
- inline关键字只是对编译器的一个建议,如果编译器发现指定的函数不适合内联就不会内联
- 内联函数必须在调用之前声明,关键字inline与函数定义体放在一起才能使函数成为内联,仅将inline放在函数声明前不起任何作用
- 在一个函数体中定义的内联函数不能再另一个文件中使用,他们通常放在头文件中共享。
- 定义在类声明中的成员函数将自动成为内联函数
-
1 class A 2 { 3 public: 4 void fun(int x,int y){}//定义在类声明中,自动的成为内联函数 5 }
将成员函数的定义放在类声明之中虽然能带来书写上的方便,但不是良好的编程风格,上例应该改为
-
1 //头文件 2 class A 3 { 4 public: 5 void fun(int x,int y); 6 } 7 //定义头文件 8 inline void A::fun(int x,int y) 9 { 10 11 }
-
类的构造函数与析构函数容易让人误以为内敛更有效,但应该注意构造函数与析构函数中可能隐藏的一些行为,如偷偷地执行了基类或成员对象的构造函数与析构函数。所以不要将构造函数与析构函数的定义放在类声明中。一个好的编译器会根据函数的定义体,自动的取消不值得的内联。
四、内联函数与宏与一般函数的区别
1.内联函数与宏的区别
- 宏代码本身不是函数而内联函数是函数
- 宏定义在预处理阶段进行代码替换,而内联函数是在编译阶段插入代码
- 宏定义没有类型检查,而内联函数有类型检查,这对于写出正确且鲁棒的程序是一个很大的优势
- 宏肯定会被展开而inline关键字修饰的函数不一定会被内联展开
2.内联函数与普通函数的区别
- 内联函数与普通函数的参数传递机制相同。内联函数与普通函数的最大区别是其内部实现的不同,普通函数在被调用时首先跳到调用函数的入口地址,执行函数体,执行完成后再回到函数调用的地方,函数始终只有一个复制。而内联函数不需要进行一个寻址过程,当执行到内联函数时,此函数被展开,如果在N处调用了此内联函数,则此函数就会有N个代码段的复制
- 两者之间时间与空间消耗的比较主要取决于调用一个函数所消耗的内存与空间与函数本身占用的内存与空间的大小有关。
3.一个程序的唯一入口main函数肯定不会被内联化。另外编译器合成的默认构造函数,复制构造函数,析构函数以及赋值运算符一般都被内联化。