[Investor Relations]  |  官方微博

C/C++培训

美国上市公司 · 亿元级外企Java培训企业

  • 全国服务监督电话400-111-8989
  • c++函数的升级

    发布:C++培训  来源:学习笔记  时间: 2015年06月04日

  • 内联函数其实是对c++编译器的一种内联请求,内联函数在最终生成的代码中是没有定义的,c++编译器直接将函数体插入到函数调用的地方,并且没有普通函数调用时的额外开销(压栈、跳转、返回),所以说它可以代替宏代码片段...

  • 1.内联函数:

    a.内联函数的来源:c++中使用const常量代替#define宏常量,如 const int a = 10; 与 #define a 10,这样就可以避免宏的副作用。那c++用什么替代宏代码片段呢?就是使用内联函数。

     

    b.内联函数其实是对c++编译器的一种内联请求,内联函数在最终生成的代码中是没有定义的,c++编译器直接将函数体插入到函数调用的地方,并且没有普通函数调用时的额外开销(压栈、跳转、返回),所以说它可以代替宏代码片段。同时内联函数是一种特殊的函数,它依然具有函数的特性(参数检查、返回类型),使用起来比宏代码片段要安全的多(内联函数是由编译器处理的,直接将编译后的函数体插入调用的地方,而宏代码片段是由预编译器处理的,进行简单文本替换,没有任何编译过程)。因为内联函数是对编译器的请求,所以编译器不一定会允许内联请求!(这个跟编译器的设置选项中的优化级别有关系)

     

    c.内联函数的形式:有两种,第一种就是在普通函数的定义和声明两处都加上inline关键字,把函数标记为内联请求。代码如下:

    inline int fun(void);

     

    inline int fun(void)

    {

    printf("hello!\n");

    return 1;

    }

     

    第二种,使用g++提供的扩展语法__attribute__((always_inline)),对函数进行强制内联,代码如下:

    inline int fun(void) __attribute__((always_inline));

     

    inline int fun(void)

    {

    printf("hello!\n");

    return 1;

    }

     

    d.c++中的内联编译的限制:

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

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

       第三:函数体不能过于庞大

       第四:不能对函数进行取址操作

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

       注意:编译器对于内联函数的限制并不是绝对的,内联函数相对于普通函数的优势只是省去了函数调用时压栈,跳转和返回的开销。因此,当函数体的执行开销远大于压栈,跳转和返回的开销时,那么内联就毫无意义了。

     

    e.内联函数的实现机制:

        当请求内联的函数,满足内联要求的时候,c++编译器就将内联函数存入符号表中,当调用这个函数的时候,就用符号表中的函数体进行替换。

     

    2.函数默认参数:

    a.函数默认参数的定义:c++中可以在函数声明时为参数提供一个默认值,当函数调用时没有指定这个参数的值,编译器会自动用默认值替代。代码如下:

    #include <stdio.h>

     

    int fun(int i = 10);

    int main()

    {

    printf("%d\n",fun());

    printf("%d\n",fun(2));

    return 0;

    }

     

    int fun(int i)

    {

    return i+i;

    }

     

    b.函数默认参数的规定:只有参数列表后面部分的参数才可以提供默认参数值,一旦在一个函数调用中开始使用默认参数值,那么这个参数后的所有参数都必须使用默认参数值

    正确代码:

    int add(int a,int b = 0,int c = 0);

    错误代码:

    int add(int a = 0,int b,int c = 0);  //因为你在调用这个add函数的时候,总不能这样吧add(1,,2);

     

    3.函数占位参数:

    a.在c++的规范中,允许使用函数占位参数,函数的占位参数在函数的声明和定义中,只有类型而没有名字,只为接受一个无用的参数而已。就像是scanf中%*d一样,起一个占位的作用。代码如下:

    #include <stdio.h>

     

    int fun(int a,int b,int);

    int main()

    {

    fun(1,2,3);

    return 0;

    }

    int fun(int a,int b,int)

    {

    return a+b;

    }

     

    b.函数占位参数有两个用途:

    第一个用途是,函数占位参数与函数默认值结合到一起使用,为以后的程序扩展留下伏笔,代码如下:

    #include <stdio.h>

     

    int fun(int a,int b,int = 0); //一般int = 0 就是为以后函数改进做准备  是一种习惯 

    int main()

    {

    fun(1,2,3);

    return 0;

    }

    int fun(int a,int b,int)

    {

    return a+b;

    }

     

    第二种用途是,兼容c语言中书写不规范的代码,因为c语言函数有一个缺陷,就是void fun()没有函数参数的函数,可以接受任意个数,任意类型的函数参数。如果要把一个定义为void fun()的函数,调用的时候写成了fun(1,2); 在工程代码中,想要移植到c++环境中,去改fun(1,2)为fun(),可能工作量会很大,所以可以直接把函数定义改成void fun(int,int),工程也可以使用了!

     

    4.函数的重载:

    a.重载的定义:用同一个函数名定义不同的函数,即当函数名和不同的参数搭配时函数的含义不同。代码如下:

    #include <stdio.h> 

    int fun(int a)

    {

    printf("aaa %d \n",a);

    }

     

    int fun(int i,int j)

    {

    printf("iii %d jjj %d\n",i,j);

    }

     

    int fun(char* s)

    {

    printf("%s\n",s);

    }

     

    int main()

    {

    fun(10);

    fun(1,2);

    fun("hello"); 

    return 0;

    }

     

    b.重载的条件(至少满足下面三个条件之一): 

       第一:参数个数不同  int fun(int a) 与  int fun(int a, int b)

       第二:参数类型不同  int fun(int a) 与  int fun(char a)

       第三:参数顺序不同  int fun(int a, char b) 与  int fun(char b, int a)

       注意:函数的返回值类型,不是重载的条件。int fun(int a)  与  char fun(int a) 这个两个函数是不能重载的

     

    c.注意在使用函数重载的时候,不要使用函数默认参数。二者不要同时使用,会出现二义性,编译器会报错!!!代码如下:

    #include <stdio.h> 

    int fun(int a)

    {

    printf("aaa %d \n",a);

    }

     

    int fun(int a,int b = 0)

    {

    printf("ab %d \n",a*b);

     

    int main()

    {

    fun(10); //此时程序会报错  因为编译器出现二义性  不知道哪个才是fun(10)应该调用的函数 

    return 0;

    }

     

    d.编译器调用重载函数的准则(就是这么多同名函数到底调用哪个):

       第一:先将所有的同名函数作为侯选者

       第二:尝试寻找可行的候选者(下面的三个条件,有顺序先后,第一种和第二种会产生二义性(属于同级),但是他们和第三种是有顺序先后的)

                  首先,精确匹配实参

                  其次,通过默认参数能够匹配实参

                  最后,通过默认类型转换匹配实参

       举个例子:对于fun(1,2)函数,它首先会去匹配Type fun(int a, int b)这类的函数,如果没有精确匹配实参;它会去匹配Type fun(int a, int b, int c = 0)或者Type fun(int a, int b, double c = 0)这类通过默认参数可以匹配的函数;如果都没有,最后可以去匹配Type fun(int a, char b)或者Type fun(double a, char b)通过默认类型转换匹配的函数。

        第三:匹配失败,有两个原因,一个是最终寻找的可行候选者函数不唯一,则出现了二义性,编译失败。另一个是无法匹配所有候选者,没有找到合适的候选者,函数未定义,编译失败。

        例子代码如下:

    #include <stdio.h> 

    int fun(int a,char b) //通过默认类型转换匹配实参

    {

    printf("aaa %d \n",a);

    }

     

    int fun(int a,int b)  //精确匹配实参

    {

    printf("hello\n");

     

    int fun(int a, int b, double c = 0)  //过默认参数能够匹配实参

    {

    printf("1234567\n");

    }

    int main()

    {

    fun(1,2);//Type fun(int a, int b)   Type fun(int a, int b, int c = 0)  Type fun(int a, double b) 

    return 0;

    }

     

    e.函数重载与函数指针:当使用重载函数名对函数指针进行赋值的时候,重载函数候选者的函数类型一定要与函数指针的函数类型完全相同,包括函数参数列表和函数返回值类型(因为函数类型是由这两个因素决定的),不然编译器会报错。代码如下:

    #include <stdio.h>   //这个程序中三个函数就没有顺序先后了  因为要函数类型完全匹配   

    typedef int (*funp)(int a);

     

    int fun(double b)

    {

    printf("hello double\n");

    }

     

    int fun(int b, int c = 0)

    {

    printf("hello bc\n");

    }

     

    int fun(int b) //此处就跟函数的返回值类型有关系了~~~~ 

    {

    printf("hello int\n");

    }

     

    int main()

    {

    funp p = fun;

    p(1);

    return 0;

    }

     

    f.函数重载的注意事项

      第一:重载函数在本质上是相互独立的不同函数

      第二:重载函数的函数类型是不同的

      第三:函数返回值不能作为函数重载的依据

      第四:函数重载是由函数名和参数列表决定的

     

    5.c++和c的相互调用:

       a. 在项目中融合c++和c代码是实际工程中不可避免的,虽然c++编译器能够兼容c语言的编译方式,但c++编译器会优先使用c++的编译方式进行编译。所有我们可以利用extern关键字强制c++编译器对代码进行c方式编译。

     

       b.c++编译的文件(main.cpp)调用c编译的文件(add.c)中的函数:

          main.cpp代码如下:

    extern "C"

    {

    #include "add.h"

    void add(int a,int b);  //在c++文件中要使用extern关键字 把所有c文件中的函数声明 都利用c编译方式进行编译 就可以了

    }

           命令:gcc add.c -o add.o 或者 gcc -c add.c       然后 g++ main.cpp add.o就可以了 

     

       c.c编译的文件(main.c)调用c++编译的文件(add.cpp)中的函数:

          add.cpp代码如下:

    extern "C"  //把cpp文件中的全部信息 都用extern关键字 进行c编译方式进行编译

    {

    int add(int a, int b)

    {

        return a + b;

    }

    }

           命令:g++ -c add.cpp   然后  gcc main.c -lstdc++ add.o  就可以了

           注意:这里说几点关于gcc和g++的问题,首先gcc和g++都可以编译c和c++的代码。只是gcc会把.c文件利用c的编译方式进行编译,而把.cpp文件利用c++的编译方式进行编译。而g++则会把.c文件和.cpp文件都使用c++的编译方式进行编译。第二点,gcc只能自动的链接标准c库,不能自动和c++程序使用的库链接。而要与c++程序使用的库链接应该使用g++或者是gcc -lstdc++

     

        d.切记:c++编译器不能以c的方式编译多个重载函数,会编译错误,代码如下:

    extern "C"  //这段代码是有问题的  是编译不过的

    {

     

    int fun(double b)

    {

    printf("hello double\n");

    }

     

    int fun(int b, int c = 0)

    {

    printf("hello bc\n");

    }

     

    int fun(int b) 

    {

    printf("hello int\n");

    }

     

    }

             但是这里面遗留了一个问题(我没有想明白):就是有好多c语言没有的特性,c++具备的,比如说引用,依然可以使用extern关键词进行c方式编译,不知道会编译成什么!!!所以我觉得最好在实际工程中,还是尽可能的用c++文件去调用c文件中的函数,别反过来。毕竟c++是c的超集,总用extern去强制编译c++的代码不一定会出现什么问题!!!

     

            e.c++和c相互调用的统一解决方案:

               __cplusplus是c++编译器内置的标准宏定义,让c代码既可以通过c编译器的编译,又可以在c++编译器中以c的方式进行编译(使用条件编译),代码如下:

    #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

    注意:g++ 不管是编译.c 文件还是 .cpp文件,都是以c++的方式编译的,都有__cplusplus这个宏。而gcc 编译.c文件时是c方式,没有__cplusplus宏,编译 .cpp文件时是c++ 方式,是有__cplusplus宏的。__cplusplus宏是否存在不是看用什么编译器,而是看是用什么编译方式!!!

  • 上一篇:C语言中指针与malloc,free的用法

    下一篇:2048控制台小游戏的制作与分析流程

相关资讯
网站导航
2001-2016 达内时代科技集团有限公司 版权所有 京ICP证8000853号-56