作用
inline是一个关键字,可以用于定义内联函数,像普通函数一样被调用,但是在调用时并不通过函数的调用机制,而是直接在调用点展开,这项可以大大减少由函数调用带来的开销,从而提高程序的运行效率
特征
相当于把内联函数里面的内容写在调用内联函数处
相当于不用执行进入函数的步骤,直接执行函数体
相当于宏,却比宏多了类型检查,真正具有函数特性
编译器一般不内联包括循环,递归,switch等复杂操作的内联函数
在类声明中定义的函数,除了虚函数的其他函数都会自动隐式地当内联函数
编译器对inline函数的处理步骤
1.将inline函数体复制到inline函数调用点处
2.为所用inline函数中的局部变量分配空间
3.将inline函数的输入参数和返回值映射到调用方法的局部变量空间中
4.如果inline函数有多个返回点,将其转变为inline函数代码块末尾的分支
inline的优缺点
优点:
1.inline定义的内联函数,函数代码被放入符号表中,在使用时进行替换,内联函数同宏函数一样在被调用处进行了代码展开,省去了参数压栈,栈帧开辟与回收,结果返回等,从而提高了程序的运行速度,效率很高
2.类的内联函数也是函数,编译器在调用一个内联函数,首先会检查参数问题,保证调用正确,像对待真正的函数一样,消除了隐患及局限性,而宏定义不会
3.inline可以作为类的成员函数,在类中同时声明定义成员函数,自动转化为内联函数,因此内联函数可以访问类的成员变量,宏定义则不能
4.内联函数在运行时可以调试,而宏定义不可以
缺点:
1.代码膨胀,内联是以代码膨胀为代价的,消除了函数调用带来的开销,如果执行函数体内代码的时间,相比于函数调用的开销大,那么效率的收获会很少,另一方面,每一处内联函数的调用都要复制代码,将使程序的总代码量增大,消耗更多的内存空间,这种情况下编译器可能会自动的把它作为非内联函数处理
2.inline函数无法随着函数库升级而升级,inline函数的改变需要重新编译,不像non-inline可以直接链接
3.是否内联,程序员不可控。内联只是对编译器的建议,对于是否内联,决定权在编译器
内联函数一般可以
1.加快程序的执行速度
2.可能减少可执行文件的大小
3.可能增加可执行文件的大小
4.可能减低执行速度
1和3很好理解,在编译时期,内联函数能将代码直接写入被调用的地方,这样就减少了入栈出栈的时间消耗,但是如果调用内联函数的地方过多,代码量也会随之增加,增加了可执行文件的大小
2为什么正确呢?因为如果调用普通函数的话编译器可能会产生更多的代码来实现压、出寄存器的代码,对于简单的内联函数会这样。但如果优化器能顺序集成消耗大量冗余代码的话,对大函数同样使用。
4.如果可执行文件过大,会频繁出现内存换入换出操作,会使执行速度下降
虚函数可以是内联函数么
1.虚函数可以是内联函数,内联是可以修饰虚函数的,但是当虚函数表现多态性的时候不能内联。
2.内联是在编译期建议编译器内敛,而虚函数的多态性在运行期,编译器无法知道运行期调用哪个代码,因此虚函数表现为多态性时,不可以内联
3.inline virtual唯一可以内联的时候是:编译器知道所调用的对象是哪个类,这只有在编译器具有实际对象而不是对对象的指针或引用时才会发生
#include <iostream>
using namespace std;
class Base
{
public:
inline virtual void who()
{
cout << "I am Base\n";
}
virtual ~Base() {}
};
class Derived : public Base
{
public:
inline void who() // 不写inline时隐式内联
{
cout << "I am Derived\n";
}
};
int main()
{
// 此处的虚函数 who(),是通过类(Base)的具体对象(b)来调用的,编译期间就能确定了,所以它可以是内联的,但最终是否内联取决于编译器。
Base b;
b.who();
// 此处的虚函数是通过指针调用的,呈现多态性,需要在运行时期间才能确定,所以不能为内联。
Base *ptr = new Derived();
ptr->who();
// 因为Base有虚析构函数(virtual ~Base() {}),所以 delete 时,会先调用派生类(Derived)析构函数,再调用基类(Base)析构函数,防止内存泄漏。
delete ptr;
ptr = nullptr;
system("pause");
return 0;
}
typedef,define,inline区别
typedef
1.类型重用名可以写在函数外部,同样也可以写在函数内部,它们的作用域不同,可以提高代码的可读性
2.typedef可以分别为基本类型重用名,指针类型重用名,结构体类型重用名和函数指针类型重用名
3.typedef是关键字,在编译时处理,有类型检查的功能,用来给一个已经存在的类型一个别名,但不能在一个函数定义里使用typedef
define
1.原理:#define作为预处理指令,在编译预处理时进行替换操作,不作正确性检查,只有在编译已被展开的源程序时才能发现可能的错误并报错,#define不仅可以为类型取别名,还可以定义常量,变量,编译开关等。
2.作用域:#define没有作用域的限制,只要是之前预定义过的宏,在以后的程序中都可以使用,而typedef有自己的作用域
3.enum给int型常量起名字,typedef给数据类型起名字,宏定义可以看作一种重用名
typedef和#define在处理指针是不完全一样
#include <iostream>
#define INTPTR1 int *
typedef int * INTPTR2;
using namespace std;
int main()
{
INTPTR1 p1, p2; // p1: int *; p2: int
INTPTR2 p3, p4; // p3: int *; p4: int *
int var = 1;
const INTPTR1 p5 = &var; // 相当于 const int * p5; 常量指针,即不可以通过 p5 去修改 p5 指向的内容,但是 p5 可以指向其他内容。
const INTPTR2 p6 = &var; // 相当于 int * const p6; 指针常量,不可使 p6 再指向其他内容。
return 0;
}
C++中推荐使用inline代替#difine声明函数