C++要点摘录(三)函数的升级

三.函数的升级(上)

      

内联函数

     C++中推荐使用内联函数替代宏代码片段

     C++中使用inline关键字声明内联函数

注:内联函数声明时inline关键字必须和函数定义结合在一起,否则编译器会直接忽略内联请求(例如和声明在一起时,会无效)


C++编译器可以将一个函数进行内联编译

被C++编译器内联编译的函数叫做内联函数

内联函数在最终生成的代码中是没有定义的

C++编译器直接将函数体插入函数调用的地方

内联函数没有普通函数调用时的额外开销(压栈,跳转,返回)


C++编译器不一定准许函数的内联请求!!


内联函数:1.内联函数是一种特殊的函数,具有普通函数的特征(参数检查,返回类型)

                     2.内联函数是对编译器的一种请求,因此编译器可能拒绝这种请求

                     3.内联函数是由编译器处理,直接将编译后的函数插入调用的地方

                                               宏代码片段由预处理器处理,进行简单的文本替换,没有任何编译过程,内联函数相对于宏代码片段,副作用更小

#include <stdio.h>

#define FUNC(a, b) ((a) < (b) ? (a) : (b))

inline int func(int a, int b)
{
    return a < b ? a : b;
}

int main(int argc, char *argv[])
{
    int a = 1;
    int b = 3;
    int c = func(++a, b);
    
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    printf("c = %d\n", c);
    
    printf("Press enter to continue ...");
    getchar();	
    return 0;
}


这段代码,使用宏代码就会副作用                             

                      4.现代编译器能够进行编译优化,因此一些函数即使没有inline声明,也可能被编译器内联编译

                    5.一些现代编译器提供了扩展语法,能够对函数进行强制内联,如: _attribute_((always_inline))属性

C++中内联编译的限制:

                     不能存在任何形式的循环语句     

                     不能存在过多的条件判断语句

                     函数体不能过于庞大

                     不能对函数进行取址操作

                            函数内联声明必须在调用语句之前

编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。

因此,当函数体的执行开销远大于压栈,跳转和返回所用的开销时,那么内联将无意义。


C++中内联函数的实现机制:将内联函数func存入符号表,用符号表中的函数体替换func调用


函数默认参数

        C++中可以在函数声明时,为参数提供一个默认值,当函数调用时没有指定这个参数的值,编译会自动用默认值代替

#include <stdio.h>

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
    
    printf("Press enter to continue ...");
    getchar();	
    return 0;
}

int mul(int x)//定义时不再指定
{
    return x * x;
}
函数默认参数的规则

          只有参数列表后面部分的参数才可以提供默认参数值       

                 . 如果一个函数中有多个默认参数,则默认参数应从右至左逐渐定义。当调用函数时,只能从左向右匹配参数

void func(int a=1,int b,int c=3, int d=4); //error
void func(int a, int b=2,int c=3,int d=4); //ok

#include <stdio.h>

int add(int a, int b = 0, int c = 0, int d = 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
    
    printf("Press enter to continue ...");
    getchar();	
    return 0;
}


默认值可以是全局变量全局常量,甚至是一个函数。例如:
    int a=1;
    int fun(int);
    int g(int x;fun(a)); //ok:允许默认值为函数

默认值不可以是局部变量,因为默认参数的函数调用是在编译时确定的,而局部变量的位置与值在编译时均无法确定。例如:

    void fun()
    {
     int i;
     void g(int x=i); //error:处理g()函数声明时,i不可见
    }


函数占位参数
          在C++中可以为函数提供占位参数
           .占位参数只有参数类型声明,而没有参数名声明
           .一般情况下,在函数体内部无法使用占位参数
#include <stdio.h>

int func(int a, int b, int)
{
    return a + b;
}

int main(int argc, char *argv[])
{
    printf("func(1, 2, 3) = %d\n", func(1, 2, 3));//必须提供三个实参,打印3
    
    printf("Press enter to continue ...");
    getchar();	
    return 0;
}


意义:可以将占位参数与默认参数结合起来使用
                 .为以后程序的扩展留下线索(函数可能增加功能)
                 .兼容C语言程序中可能出现的不规范写法
#include <stdio.h>

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 ...");
    getchar();	
    return 0;
}


小结:C++中可以通过inline声明内联函数
            内联函数在编译时直接将函数体插入函数调用的地方
            inline只是一种请求,编译器不一定允许这种请求
            内联函数省去了普通函数调用时压栈,跳转和返回的开销
            C++中在声明函数的时候指定参数的默认值
            C++可以声明占位符参数,占位符参数一般用于程序扩展和对C代码的兼容


函数的升级(下)

          
        重载的概念(over load)

              同一个标识符在不同的上下文有不同的意义

       函数重载(Function  Overload)
          用同一个函数名定义不同的函数
          当函数名和不同的参数搭配时函数的含义不同
      函数重载至少满足下面的一个条件:
          参数个数不同
          参数类型不同
          参数顺序不同
#include <stdio.h>
#include <string.h>

int func(int x)
{
    return x;
}

int func(int a, int b)
{
    return a + b;
}

int func(const char* s)
{
    return strlen(s);
}

int func(int a, const char* s)
{
    return a;
}

int func(const char* s, int a)
{
    return strlen(s);
}

int main(int argc, char *argv[])
{
    int c = 0;
    
    c = func("ab", 1);
    
    printf("c = %d\n", c);//c = 2
    
    printf("Press enter to continue ...");
    getchar();	
    return 0;
}

      当函数默认参数遇上函数重载会发生什么?

#include <stdio.h>
#include <string.h>

int func(int a, int b, int c = 0)
{
    return a * b * c;
}

int func(int a, int b)
{
    return a + b;
}

int main(int argc, char *argv[])
{
    int c = 0;
    
    c = func(1, 2); //存在二义性,调用失败,编译不能通过
    
    printf("c = %d\n", c);
    
    printf("Press enter to continue ...");
    getchar();	
    return 0;
}

编译器调用重载函数的准则

     .将所有函数名作为候选者
     .尝试寻找可行的候选函数:1.精确匹配实参;2.通过默认参数能够匹配实参;3.通过默认类型转换匹配实参
     .匹配失败:1.最终寻找到的可行候选函数不唯一,则出现二义性,编译失败;2.无法匹配所有候选者,函数未定义,编译失败

函数重载的注意事项:

     重载函数在本质上是相互独立的不同函数
     重载函数的函数类型是不同的
     函数返回值不能作为函数重载的依据,函数重载是由函数名和参数列表决定的

以下三个函数能够成重载
int biggest(int a,int b,int c);
{
	......
}
float biggest(float a,float b)
{
	......
}
float biggest(float a,float b,float c)
{
	......
}
以下不能重载
int biggest(int a,int b);
{
	......
}
float biggest(int a,int b)
{
	......
}

      函数重载,只看参数个数,类型和顺序,不看返回值。重载的函数,虽然函数名相同, 但是编译会根据参数将其编译成不同的函数名,比如上面可能会编译成biggest_int_int_int, biggest_float_float, biggest_float_float_float,因此,只要参数不同,是可以的

函数重载与函数指针:
    当使用重载函数名对函数指针进行赋值时,根据重载规则挑选与函数指针参数列表一致的候选者,严格匹配候选者的函数返回值类型与函数指针的函数返回值类型

#include <stdio.h>
#include <string.h>

int func(int x) // int(int a)
{
    return x;
}

int func(int a, int b)
{
    return a + b;
}

int func(const char* s)
{
    return strlen(s);
}

typedef int(*PFUNC)(int a); // int(int a)

int main(int argc, char *argv[])
{
    int c = 0;
    PFUNC p = func;
    
    c = p(1);
    
    printf("c = %d\n", c);//c = 1
    
    printf("Press enter to continue ...");
    getchar();	
    return 0;
}

C++和C的相互调用
     .在项目中融合C++和C代码是实际工程中不可避免的
     .虽然C++编译器能够兼容C语言的编译方式,但C++编译器会优先使用C++的方式进行编译
     .利用extern关键字强制让C++编译器对代码进行C方式编译
在C调用的部分使用external “c”来修饰


统一解决方案:. _cplusplus是C++编译器内置的标准宏定义
                            . _cplusplus让C代码即可以通过C编译器的编译,也可以在C++编译器中以C的方式编译

#ifdef __cplusplus
extern "C" {
#endif



#ifdef __cplusplus
}
#endif

注意:C++编译器不能以C的方式编译个多重载函数
#include <stdio.h>
#include <string.h>


#ifdef __cplusplus
extern "C" {
#endif

int func(int a, int b)
{
    return a + b;
}

int func(const char* s)
{
    return strlen(s);
}

#ifdef __cplusplus
}
#endif

int main(int argc, char *argv[])
{
    printf("Press enter to continue ...");
    getchar();	
    return 0;
}


会报错,函数声明冲突!!!


注:
       extern C 指的标识符的编译方式,不会干涉编译器的编译行为
所以在C++编译器下面  仍然是 C++的行为不会有所改变(例如const常量依旧会放入符号表)

     标识符的编译方式是指,func(int a, int b)这个函数,在C编译器里面,可能会被编译成_func()
在C++编译器里面,会被编译成_func_int_int,加了extern 后就会编译成_func()C语言的编译器连接器就能识别了


     C++就是靠这个方式来区别重载函数的,所以如果用了C的方式,func(int a, int b)和func(char ,int)都是命名成_func,不能区分了,所以不能用   



小结:.函数重载是C++对C语言的一个重要升级
            .函数重载通过函数参数列表区分不同的同名函数
            .函数的返回值类型不是函数重载的依据
            .extern关键字能够实现C和C++的相互调用
    


extern C 指的标识符的编译方式
不会干涉编译器的编译行为
所以在C++编译器下面  仍然是C++的行为不会有所改变

标识符的编译方式是指,func(int a, int b)这个函数,在C编译器里面,可能会被编译成_func()
在C++编译器里面,会被编译成_func_int_int

C++就是靠这个方式来区别重载函数的,所以如果用了C的方式,func(int a, int b)和func(char ,int)都是命名成_func,不能区分了,所以不能用   

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值