• C++中的内联函数分析


    1,本节课学习 C++ 中才引入的新的概念,内联函数;

     

    2,常量与宏回顾:

        1,C++ 中的 const 常量可以替代宏常数定义,如:

           1,const int A = 3; <==> #define A 3

           2,C++ 中如果要使用宏常数,则可以用 const 常数代替;

        2,C++ 中是否有解决方案替代宏代码片段呢?

           1,为了替换宏代码块,内联函数的概念被提出;

     

    3,内联函数用法:

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

           1,宏代码块看上去像函数但实际不是函数,因此其使用常带有副作用;

           2,消除这样的副作用需要函数,但是函数的调用有入栈返回等开销,这一点宏代码块没有;

           3,综合两者有点,避免两者缺点,内联函数被提出来;

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

    1 inline int func(int a, int b)
    2 {
    3      return a < b ? a : b;
    4 }

           1,内联函数在表现形式上和普通函数一样,但是它会被编译器进行优化,编译器直接将内联函数的函数体进行扩展,这个扩展由编译器直接将生成的代码进行扩展,扩展到调用这个内联函数的地方,由编译器进行,所以会进行编译器检查等一些列工作;

           2,inline 可以将一个函数声明为内联函数,但是这种声明是对编译器的一种请求,请求编译器将这个函数进行内联编译,所以编译器可以拒绝内联请求;

           3,内联函数声明时 inline 关键字必须和函数定义结合在一起,否则编译器会直接忽略内敛请求;

     

    4,内联函数特点:

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

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

           1,内联函数在 C++ 中的地位是用来替换 C 中的宏代码块;

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

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

           1,C++ 编译器不一定满足函数的内敛请求;  

               1,此时内联函数变为普通函数,行为同普通函数;

     

    5,内联函数初探编程实验:

        1,main.cpp 文件:

     1 #include <stdio.h>
     2 
     3 /* 宏代码块 */
     4 #define FUNC(a, b) ((a) < (b) ? (a) : (b))  
     5 
     6 /* 声明一个内联函数 */
     7 inline int func(int a, int b)  
     8 {
     9     return a < b ? a : b;
    10 }
    11 
    12 int main(int argc, char *argv[])
    13 {
    14     int a = 1;
    15     int b = 3;
    16     int c = FUNC(++a, b); // ==> int c = ((++a) < (b) ? (++a) : (b)); 这里 a 加了两次;
    17   
    18     // int c = func(++a, b);
    19     
    20     printf("a = %d
    ", a);
    21     printf("b = %d
    ", b);
    22     printf("c = %d
    ", c);
    23     
    24     return 0;
    25 }

        1,宏调用结果:

           a = 3  b = 3  c = c 

        2,函数调用结果:

          a = 2  b = 3  c = 2 

        3,VC 工程属性优化 inline 默认:

     

        4,VC 工程属性优化 inline 内联:

     

        5,Eclipse 工程 g++ 编译 inline 内联:

    2,结论:

      1,内联函数函数请求有可能被拒绝;

      2,内联函数是存在的,可以替换宏代码块;

     

    6,内联函数特征:

        1,内联函数具有普通函数的特征(参数检查,返回类型等);

           1,类型更加安全;

           2,C++ 提供各种方式让大家摒弃 C 中不好的一些特性,内联函数就是其中之一;

        2,函数的内联请求可能被编译器拒绝;

           1,可以通过配置的方式让编译器支持内联函数的请求;

        3,函数被内联编译后,函数体直接扩展到调用的地方;

           1,如果内联成功,其效率上可以和宏代码块媲美,且其在类型上比宏代码块安全很多,因此在 C++ 编程中,首选内联函数;

           2,宏代码片段由预处理器处理,进行简单的文本替换,没有任何编译过程,因此可能出现副作用;

     

    7,现代 C++ 编译器对内联函数的行为:

        1,现代 C++ 编译器能够进行编译优化,一些函数即使没有 inline 声明,也可能被内联编译;

           1,内联函数可能被拒绝是因为 C++ 诞生年代相对于现在比较早,当时编译 技术未有达到现在先进的技术,因此对于一些复杂的内联函数,C++ 编译器没有能力将函数体直接扩展到调用的地方,所以 inline 关键字在当时设计成了对编译器的请求;

        2,一些现代 C++ 编译器提供了扩展语法,能够对函数进行强制内联,如:

           1,g++:__attribute__((always_inline)) 属性;

               1,不是标准 C++ 属性;

           2,MAVC:__forceinline;

     

    8,内联函数深度示例编程实验:

        1,main.cpp 文件:

     1 #include <stdio.h>
     2 
     3 __forceinline
     4 // __attribute__((always_inline))
     5 // inline 
     6 int add_inline(int n);
     7 
     8 int main(int argc, char *argv[])
     9 {
    10     int r = add_inline(10);  // 关注此行;
    11 
    12     printf(" r = %d
    ", r);
    13 
    14     return 0;
    15 }
    16 
    17 inline int add_inline(int n)
    18 {
    19     int ret = 0;
    20 
    21     for(int i=0; i<n; i++)
    22     {
    23         ret += i;
    24     }
    25 
    26     return ret;
    27 }

    1,inline 时:

         1,VC 默认的,不内联,设置后,就内联;

         2,g++ 默认的,不内联,设置后,就内联;

    3,__forceinline,内联:

         1,对于 VC++ 编译器,forceinline 和 inline 功能几乎是一模一样,只不过后者是标准 C++ 支持的,可移植性更好;

    4,__attribute__((always_inline)):

         1,对于 g++ 编译器,内联;

     

    9,注意事项:

        1,C++ 中 inline 内联编译的限制:

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

               1,这个限制现在也没有了;

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

           3,函数体不能过于庞大;

           4,不能对函数进行取址操作;

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

        2,总的来说就是函数体不能过于复杂,这个复杂没有界定,对于现在 C++ 编译器,只要函数不是太夸张,几乎都可以满足请求;

     

    10,小结:

        1,C++ 中可以通过 inline 声明内联函数;

        2,编译器直接将内联函数体扩展到函数调用的地方;

        3,inline 只是一种请求,编译器不一定允许这种请求;

        4,内联函数省去了函数调用栈时压栈,跳转和返回的开销;

    此文为作者学习唐佐林老师的学习笔记,仅为交流共享之用,由此带来的后果,与作者无关;转载请注明转载出处;难免有错,欢迎指正,联系方式qunchao24@sina.com。
  • 相关阅读:
    对list集合中的对象进行排序(转载)
    关键字的作用
    CocoaPods的 安装 /卸载/升级
    block基本使用和底层
    程序启动 - 类调用的方法
    成员变量修饰词的作用
    宏(define)与常量(const)
    iOS
    监听网络状态
    nil、Nil、NULL与NSNull的区别及应用
  • 原文地址:https://www.cnblogs.com/dishengAndziyu/p/10902908.html
Copyright © 2020-2023  润新知