Skip to content

Latest commit

 

History

History
64 lines (45 loc) · 6.19 KB

File metadata and controls

64 lines (45 loc) · 6.19 KB

C++有三种方式实现内联函数:

  1. 将函数的定义写在类定义的内部;
  2. 在类定义内部的函数声明上用 inline 显式指定;
  3. 在类定义体外部的函数实现上用 inline 显式指定;
#include <iostream>

using namespace std;

class Sample
{
public:
    void inline01(){cout<<"";}
    inline char inline02() const;
    int inline03();
};

char Sample::inline02() const
{
    return 'w';
}

inline int inline03()
{
    return 0;
}

但是需要注意的是 inline 并不是强制的,编译器可以决定标有 inline 的函数不做 inline,程序里的 inline 或者上面的任何三种写法都是一个 hint,但是编译器可以不采纳,virtual 和 inline 也可以一起使用,但这时 inline 就没什么作用了。

内联函数:

Tip: 只有当函数只有 10 行甚至更少时才将其定义为内联函数.

定义: 当函数被声明为内联函数之后, 编译器会将其内联展开, 而不是按通常的函数调用机制进行调用. 优点: 当函数体比较小的时候, 内联该函数可以令目标代码更加高效. 对于存取函数以及其它函数体比较短, 性能关键的函数, 鼓励使用内联. 缺点: 滥用内联将导致程序变慢. 内联可能使目标代码量或增或减, 这取决于内联函数的大小. 内联非常短小的存取函数通常会减少代码大小, 但内联一个相当大的函数将戏剧性的增加代码大小. 现代处理器由于更好的利用了指令缓存, 小巧的代码往往执行更快。结论: 一个较为合理的经验准则是, 不要内联超过 10 行的函数. 谨慎对待析构函数, 析构函数往往比其表面看起来要更长, 因为有隐含的成员和基类析构函数被调用! 另一个实用的经验准则: 内联那些包含循环或 switch 语句的函数常常是得不偿失 (除非在大多数情况下, 这些循环或 switch 语句从不被执行). 有些函数即使声明为内联的也不一定会被编译器内联, 这点很重要; 比如虚函数和递归函数就不会被正常内联. 通常, 递归函数不应该声明成内联函数.(递归调用堆栈的展开并不像循环那么简单, 比如递归层数在编译时可能是未知的, 大多数编译器都不支持内联递归函数). 虚函数内联的主要原因则是想把它的函数体放在类定义内, 为了图个方便, 抑或是当作文档描述其行为, 比如精短的存取函数.

-inl.h 文件:

Tip: 复杂的内联函数的定义, 应放在后缀名为 -inl.h 的头文件中.

内联函数的定义必须放在头文件中, 编译器才能在调用点内联展开定义. 然而, 实现代码理论上应该放在 .cc 文件中, 我们不希望 .h 文件中有太多实现代码, 除非在可读性和性能上有明显优势. 如果内联函数的定义比较短小, 逻辑比较简单, 实现代码放在 .h 文件里没有任何问题. 比如, 存取函数的实现理所当然都应该放在类定义内. 出于编写者和调用者的方便, 较复杂的内联函数也可以放到 .h 文件中, 如果你觉得这样会使头文件显得笨重, 也可以把它萃取到单独的 -inl.h 中. 这样把实现和类定义分离开来, 当需要时包含对应的 -inl.h 即可。

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

为保证不会发生这样的事情,建议把 inline 函数的定义放到头文件中。在每个调用该 inline 函数的文件中包含该头文件。这种方法保证对每个 inline 函数只有一个定义,且程序员无需复制代码,并且不可能在程序的生命期中引起无意的不匹配的事情。 D 正确。定义在类声明之中的成员函数将自动地成为内联函数,例如: class A { public:void Foo(int x, int y) { ... } // 自动地成为内联函数 }

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

函数名与函数指针

void MyFun(int x); //这个申明也可写成:void MyFun( int );
void (*FunP)(int ); //也可申明成 void(*FunP)(int x),但习惯上一般不这样。结论:

  1. 其实,MyFun 的函数名与 FunP 函数指针都是一样的,即都是函数指针。MyFun 函数名是一个函数指针常量,而 FunP 是一个函数数指针变量,这是它们的关系。
2. 但函数名调用如果都得如(*MyFun)(10);这样,那书写与读起来都是不方便和不习惯的。所以 C 语言的设计者们才会设计成又可允许 MyFun(10);这种形式地调用(这样方便多了并与数学中的函数形式一样,不是吗?)。
3. 为统一起见,FunP 函数指针变量也可以 FunP(10)的形式来调用。
4. 赋值时,即可 FunP=&MyFun 形式,也可 FunP=MyFun。