zoukankan      html  css  js  c++  java
  • C++ inline

     内联函数相对于宏的区别和优点:

    宏是在预处理时进行的机械替换,内联是在编译时进行的。内联函数是真正的函数,只是在调用时,没有调用开销,像宏一样进行展开。内联函数会进行参数匹配检查,相对于带参数的宏有很好的优点,避免了处理宏的一些问题。宏无法进行调试


    inline

    1、产生背景

    写函数的好处:第一,它使程序更可读;第二,它使这段代码可以重复使用。但是,它也有缺点:当它被频繁地调用的时候,由于调用函数的开销,会对应用程序的性能有损失。

    宏有很多的不尽人意的地方。

    1、宏不能访问对象的私有成员。

    2、宏的定义很容易产生二意性。

    3、使用宏,无法进行调试,虽然windows提供了ASSERT宏

    表达式形式的宏定义一例:
       #define ExpressionName(Var1,Var2) (Var1+Var2)*(Var1-Var2)
         这种表达式形式宏形式与作用跟函数类似,但它使用预编译器,没有堆栈,使用上比函数高效。但它只是预编译器上符号表的简单替换,不能进行参数有效性检测及使用C++类的成员访问控制。
    inline 推出的目的,也正是为了取代这种表达式形式的宏定义,它消除了它的缺点,同时又很好地继承了它的优点。inline代码放入预编译器符号表中,高效;它是个真正的函数,调用时有严格的参数检测;它也可作为类的成员函数。

    2 、具体作用
    直接在class类定义中定义各函数成员,系统将他们作为内联函数处理;成员函数是内联函数,意味着:每个对象都有该函数一份独立的拷贝。
    在类外,如果使用关键字inline定义函数成员,则系统也会作为内联函数处理;

    内联函数和宏的区别在于,宏是由预处理器对宏进行替代,而内联函数是通过编译器控制来实现的。而且内联函数是真正的函数,只是在需要用到的时候,内联函数像宏一样的展开,所以取消了函数的参数压栈,减少了调用的开销。你可以象调用函数一样来调用内联函数,而不必担心会产生于处理宏的一些问题。

    inline的原理:代码替代    在程序编译时,编译器将程序中出现的内联函数的调用表达式用内联函数的函数体来进行替代。

    3、inline的使用

           让一个函数成为内联函数,隐式的为在类里定义函数,显式的则是在函数前加上inline关键字说明。

    4、使用inline的一些注意事项

          a.从inline的原理,我们可以看出,inline的原理,是用空间换取时间的做法,是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。所以,如果函数体代码过长或者函数体重有循环语句,if语句或switch语句或递归时,不宜用内联

          b.关键字inline 必须与函数定义体放在一起才能使函数成为内联,仅将inline 放在函数声明前面不起任何作用。内联函数调用前必须声明。《高质量C/C++编程》里一个例子。

    inline void Foo(int x, int y); // inline 仅与函数声明放在一起
    void Foo(int x, int y)
    {
        ...
    }

    以上代码不能成为内联函数,而以下则可以

    void Foo(int x, int y);
    inline void Foo(int x, int y) // inline 与函数定义体放在一起
    {
        ...
    }

    所以说,inline 是一种“用于实现的关键字”,而不是一种“用于声明的关键字”。对于以上例子,林锐还建议,只在定义前加上inline,而不是在声明和定义前都加,因为这能体现高质量C++/C 程序设计风格的一个基本原则:声明与定义不可混为一谈。

           c.inline对于编译器来说只是一个建议,编译器可以选择忽略该建议。换句话说,哪怕真的写成了inline,也没有任何错误的情况下,编译器会自动进行优化。所以当inline中出现了递归,循环,或过多代码时,编译器自动无视inline声明,同样作为普通函数调用。

     

    class Fred {public:   
    void f(int i, char c);}; 

    但是当你定义内联成员函数时,在成员函数定义前加上 inline 关键字,并且将定义放入头文件中:inlinevoid Fred::f(int i, char c){   // ...}通常将函数的定义({...}之间的部分)放在头文件中是强制的。如果你将内联函数的定义放在 .cpp 文件中并且在其他 .cpp 文件中调用它,连接器将给出“unresolved external”错误。

    使用内联函数时应注意以下几个问题:
      (1) 在一个文件中定义的内联函数不能在另一个文件中使用。它们通常放在头文件中共享。
      (2) 内联函数应该简洁,只有几个语句,如果语句较多,不适合于定义为内联函数。 
      (3) 内联函数体中,不能有循环语句、if语句或switch语句,否则,函数定义时即使有inline关键字,编译器也会把该函数作为非内联函数处理。
      (4) 内联函数要在函数被调用之前声明。例如下面的代码将内联函数放在函数调用之后声明,不能起到预期的效果。注意,这里是定义之前,不仅仅是声明之前。对于普通函数,可以在调用之前声明,调用代码之后具体定义(实现函数),但是内联函数要实现内联,必须先定义再调用,否则编译器会把在定义之前调用的内联函数当做普通函数进行调用。

    #include <iostream.h>
    int increment(int i);
    inline increment(int i)
    {
     i++;
     return i;
    }
    void main()
    {
     int i=0;
     while(i<3)
     {
      i=increment(i);
      cout<<"i is "<<i<<endl;
     }
    }
    这段程序的输出结果为:
    i is 1
    i is 2
    i is 3

    如果我们修改一下程序,将内联函数的定义移到main()之后:

    #include <iostream.h>
    int increment(int i);
    void main()
    {
     int i=0;
     while(i<3)
     {
      i=increment(i);
      cout<<"i is "<<i<<endl;
     }
    }
    //内联函数定义放在main()函数之后
    inline int increment(int i)
    {
     i++;
     return i;
    }

      内联函数在调用之后才定义,这段程序在编译的时候编译器不会直接把它替换到main中。也就是说实际上"increment(int i)"只是作为一个普通函数被调用,并不具有内联函数的性质,无法提高运行效率。


    const
    一、产生背景

    a C++有一个类型严格的编译系统,这使得C++程序的错误在编译阶段即可发现许多,从而使得出错率大为减少,因此,也成为了C++与C相比,有着突出优点的一个方面。
    b C中很常见的预处理指令 #define VariableName VariableValue 可以很方便地进行值替代,这种值替代至少在三个方面优点突出:
    一是避免了意义模糊的数字出现,使得程序语义流畅清晰,如下例:
      #define USER_NUM_MAX 107 这样就避免了直接使用107带来的困惑。
    二是可以很方便地进行参数的调整与修改,如上例,当人数由107变为201时,改动此处即可;
    三是提高了程序的执行效率,由于使用了预编译器进行值替代,并不需要为这些常量分配存储空间,所以执行的效率较高。然而,预处理语句虽然有以上的许多优点,但它有个比较致命的缺点,即,预处理语句仅仅只是简单值替代,缺乏类型的检测机制。这样预处理语句就不能享受C++严格类型检查的好处,从而可能成为引发一系列错误的隐患。

    Const 推出的初始目的,正是为了取代预编译指令,消除它的缺点,同时继承它的优点。现在它的形式变成了:

    Const DataType VariableName = VariableValue ;
    二、具体作用
    1.const 用于指针的两种情况分析:
         int const *A;  //A可变,*A不可变
         int *const A;  //A不可变,*A可变
     分析:const 是一个左结合的类型修饰符,它与其左侧的类型修饰符和为一个
    类型修饰符,所以,int const 限定 *A,不限定A。int *const 限定A,不限定*A。
    2.const 限定函数的传递值参数:
         void Fun(const int Var);
         分析:上述写法限定参数在函数体中不可被改变。
    3.const 限定函数的值型返回值:
    const int Fun1();
    const MyClass Fun2();
         分析:上述写法限定函数的返回值不可被更新,当函数返回内部的类型时(如Fun1),已经是一个数值,当然不可被赋值更新,所以,此时const无意义,最好去掉,以免困惑。当函数返回自定义的类型时(如Fun2),这个类型仍然包含可以被赋值的变量成员,所以,此时有意义。
    4. 传递与返回地址: 此种情况最为常见,由地址变量的特点可知,适当使用const,意义昭然。
    5. const 限定类的成员函数:
    class ClassName {
     public:
      int Fun() const;
     .....
    }
      注意:采用此种const 后置的形式是一种规定,亦为了不引起混淆。在此函数的声明中和定义中均要使用const,因为const已经成为类型信息的一部分。
    获得能力:可以操作常量对象。
    失去能力:不能修改类的数据成员,不能在函数中调用其他不是const的函数。

     

  • 相关阅读:
    sqlsever2008及以上各个安装包的说明
    解决 windows2012 下无法安装 sql2008R2
    dapper extensions (predicates)
    Dapper full example
    Dapper.ColumnMapper 的使用
    wms
    大端格式 与 小端格式
    mysql数据库引擎
    事务
    MySQL索引底层实现
  • 原文地址:https://www.cnblogs.com/balingybj/p/4764716.html
Copyright © 2011-2022 走看看