C++中inline用法详解

c/c++

浏览数:105

2019-7-20

一、为什么使用inline函数?

1.1为了解决一些频繁调用的小函数大量消耗栈空间(栈内存)。

在预编译的时候,编译器将程序中出现的内联函数的调用表达式的地方直接插入用内联函数的代码。

1.2用它替代C中表达式形式的宏定义(macros)。1

对于形似函数的宏,最好改用inline函数替换#define。

    //标准的max template(来自<algorithm>)往往是这样实现出来的:
    template <typename T>
    inline const T& std::max(const T& a,const T& b)
    {return a<b?b:a}

二、inline的使用与限制

2.1如果想把一个函数定义为内联函数,则需要在函数名前面放置关键字 inline,内联函数的定义必须出现在内联函数第一次调用之前。

2.2关键字 inline 必须与函数定义体放在一起才能使函数成为内联,仅将 inline 放在函数声明前面不起任何作用。

如下风格的函数 Foo 不能成为内联函数:

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

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

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

tips:尽管在大多数教科书中内联函数的声明、定义体前面都加了inline 关键字,但我认为inline不应该出现在函数的声明中。这个细节虽然不会影响函数的功能,但是体现了高质量C++/C 程序设计风格的一个基本原则:声明与定义不可混为一谈,用户没有必要、也不应该知道函数是否需要内联。

2.3建议 inline 函数的定义放在头文件中。2

Inline函数通常一定被置于头文件内,因为大多数建置环境在编译过程中进行内联,而为了将一个“函数调用”替换为“被调用函数的本体”,编译器必须知道那个函数长什么样子。某些建置环境可以在连接器完成内联,少数建置环境竟可以在运行期完成内联。然而这样的环境毕竟是例外,不是通利。

声明跟定义要一致:如果在每个文件里都实现一次该内联函数的话,那么,最好保证每个定义都是一样的,否则,将会引起未定义的行为。如果不是每个文件里的定义都一样,那么,编译器展开的是哪一个,那要看具体的编译器而定。所以,最好将内联函数定义放在头文件中。

2.4在内联函数内不允许使用循环语句和开关语句。

内联那些包含循环或 switch 语句的函数常常是得不偿失 (除非在大多数情况下, 这些循环或 switch 语句从不被执行).

有些函数即使声明为内联的也不一定会被编译器内联, 这点很重要; 比如虚函数和递归函数就不会被正常内联. 通常, 递归函数不应该声明成内联函数.(递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译时可能是未知的, 大多数编译器都不支持内联递归函数). 虚函数内联的主要原因则是想把它的函数体放在类定义内, 为了图个方便, 抑或是当作文档描述其行为, 比如精短的存取函数。

三、inline仅是一个对编译器的建议

inline 函数仅仅是一个对编译器的建议而已,并不是说声明了内联就会内联。所以最后能否真正内联,看编译器的意思,它如果认为函数不复杂,能在调用点展开,才会真正内联。因为,编译器比程序设计者更清楚对于一个特定的函数是否合适进行内联扩展;一些情况下,对于程序员指定的某些内联函数,编译器可能更倾向于不使用内联甚至根本无法完成内联。

四、C++ 内联函数是通常与类一起使用。

在类定义中的定义的函数(类内部)都是内联函数,即使没有使用 inline 说明符。如果在类中未给出成员函数定义,而又想内联该函数的话,那在类外要加上 inline,否则就认为不是内联的。值得注意的是:如果在类体外定义inline函数,则心须将类定义和成员函数的定义都放在同一个头文件中,否则编译时无法进行置换。

五、慎用 inline

如果执行函数体内代码的时间,相比于函数调用的开销较大,那么效率的收获会很少。另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间。

要当心构造函数和析构函数可能会隐藏一些行为,如“偷偷地”执行了基类或成员对象的构造函数和析构函数。如果将类的析构函数定义为内联函数,可能会导致潜在的代码膨胀。

  1. [inline与#define的区别]
  2. [头文件怎么写?]

作者:撒把豆子