zoukankan      html  css  js  c++  java
  • inline-内联函数的优点以及与宏定义的区别

    inline函数的优点:

      C++ 语言的函数内联机制既具备宏代码的效率,又增加了安全性,而且可以自由操作类的数据成员。所以在C++ 程序中,应该用内联函数取代所有宏代码.

    inline函数与宏定义的区别:

    1.宏定义只是简单的文本替换,不做任何安全性的检查也不像函数那样在栈上创建新的变量空间.

      (1)宏定义可能会造成cxy不希望的变量多次累加;在下面F宏定义中基本上是x出现了几次就会被累加了几次.

    #define F(x) x+x
    #define F3(x) x+x+x
        int i = 0;
        cout<<F(i++)<<endl;//1  <=> (i++ + i ++) i is added twice
        cout<<i<<endl;//2
        int j = 0;
        cout<<F3(j++)<<endl;    //3
        cout<<j<<endl; //3

      (2)没有类型检查,可能传进来任意类型.

    #define MAX(a,b) ((a)>(b)?(a):(b))
    MAX(a,"Hello"); //错误地比较int和字符串,没有参数类型检查

    2.宏定义无法操作类的私有成员,而inline函数可以.

      综上,C++ 语言的函数内联机制既具备宏代码的效率,又增加了安全性,而且可以自由操作类的数据成员。所以在C++ 程序中,应该用内联函数取代所有宏代码.

      BUT , “断言assert”恐怕是唯一的例外。assert是仅在Debug版本起作用的宏,它用于检查“不应该”发生 的情况。为了不在程序的Debug版本和Release版本引起差别,assert不应该产生任何副作用。如果assert是函数,由于函数调用会引起内 存、代码的变动,那么将导致Debug版本与Release版本存在差异。所以assert不是函数,而是宏。

    inline函数用法:

    1.inline用于实现的关键字并不是生成类型关键字.比如如下定义并不起作用:  

    inline void foo();// inline仅与函数声明放在一起 
    void foo()
    {
    }

    而如下风格的函数Foo则成为内联函数: 

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

    2.定义在类声明之中的成员函数将自动地成为内联函数(类声明与实现分离与否的重要区别),例如 

    class A 
    { 
    public: 
    void Foo(int x, int y) { … }  // 自动地成为内联函数 
    } 

    inline慎用:

      内联是以代码膨胀(复制)为代价,仅仅省去了函数调用的开销,从而提高函数的执行效率。如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率 的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。以下情况不宜使用内联: 
    (1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。 
    (2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。 
    类的构造函数和析构函数容易让人误解成使用内联更有效。要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。所以不要随便地将构造函数和析构函数的定义体放在类声明中。 
      一个好的编译器将会根据函数的定义体,自动地取消不值得的内联(这进一步说明了inline不应该出现在函数的声明中)。

    例题一道:

    关于c++的inline关键字,以下说法正确的是()
    正确答案: D 
    
    使用inline关键字的函数会被编译器在调用处展开 (不移地ing)
    头文件中可以包含inline函数的声明
    可以在同一个项目的不同源文件内定义函数名相同但实现不同的inline函数
    定义在Class声明内的成员函数默认是inline函数
    优先使用Class声明内定义的inline函数
    优先使用Class实现的内inline函数的实现

      内联不是你想展想展就能展!

    ANs:D

    A 如果只声明含有inline关键字,就没有内联的效果。 内联函数的定义必须放在头文件中, 编译器才能在调用点内联展开定义.   有些函数即使声明为内联的也不一定会被编译器内联, 这点很重要; 比如虚函数和递归函数就不会被正常内联. 通常, 递归函数不应该声明成内联函数.
    B 内联函数应该在头文件中定义,这一点不同于其他函数。编译器在调用点内联展开函数的代码时,必须能够找到 inline 函数的定义才能将调用函数替换为函数代码,而对于在头文件中仅有函数声明是不够的。
    C 当然内联函数定义也可以放在源文件中,但此时只有定义的那个源文件可以用它,而且必须为每个源文件拷贝一份定义(即每个源文件里的定义必须是完全相同的),当然即使是放在头文件中,也是对每个定义做一份拷贝,只不过是编译器替你完成这种拷贝罢了。但相比于放在源文件中,放在头文件中既能够确保调用函数是定义是相同的,又能够保证在调用点能够找到函数定义从而完成内联(替换)。
    对于由两个文件compute.C和draw.C构成的程序来说,程序员不能定义这样的min()函数,它在compute.C中指一件事情,而在draw.C中指另外一件事情。如果两个定义不相同,程序将会有未定义的行为:

          为保证不会发生这样的事情,建议把inline函数的定义放到头文件中。在每个调用该inline函数的文件中包含该头文件。这种方法保证对每个inline函数只有一个定义,且程序员无需复制代码,并且不可能在程序的生命期中引起无意的不匹配的事情。

    D 正确。 定义在类声明之中的成员函数将自动地成为内联函数,例如:
    1
    class A {   public:  void Foo(int x, int y) { ... }   // 自动地成为内联函数   } 
     
    EF 在每个调用该inline函数的文件中包含该头文件。这种方法保证对每个inline函数只有一个定义,且程序员无需复制代码,并且不可能在程序的生命期中引起无意的不匹配的事情。最好只有一个定义!
  • 相关阅读:
    SPOJ GSS4 Can you answer these queries IV ——树状数组 并查集
    SPOJ GSS3 Can you answer these queries III ——线段树
    SPOJ GSS2 Can you answer these queries II ——线段树
    SPOJ GSS1 Can you answer these queries I ——线段树
    BZOJ 2178 圆的面积并 ——Simpson积分
    SPOJ CIRU The area of the union of circles ——Simpson积分
    HDU 1724 Ellipse ——Simpson积分
    HDU 1071 The area ——微积分
    HDU 4609 3-idiots ——FFT
    BZOJ 2194 快速傅立叶之二 ——FFT
  • 原文地址:https://www.cnblogs.com/luntai/p/6251727.html
Copyright © 2011-2022 走看看