一、内联函数
普通的函数在调用的时候会开辟函数栈帧,会产生一定量的消耗,在C语言中可以用宏函数来解决这个问题,但是宏存在以下缺陷:复杂、容易出错、可读性差、不能调试。为此,C++中引入了内联函数这种方法。
1.1 定义
以inline修饰的函数叫做内联函数,编译时C++编译器会在调用内联函数的地方展开,没有函数调用建立栈帧的开销,所以内联函数可以提高程序的运行效率。
🪆普通函数:
int Add(int x, int y)//这里的Add是一个普通函数 { return x + y ; } int main() { int ret = 0; ret = Add(3, 5); cout << ret << endl; return 0; }
🪆内联函数:
inline int Add(int x, int y) { return x + y ; } int main() { int ret = 0; ret = Add(3, 5); cout << ret << endl; return 0; }
注意:在默认的Debug模式下,内联函数是不会展开的,需要进行设置,设置过程如下:
1.2 特性
inline是一种以时间换空间的做法,如果编译器将函数当成内联函数处理,在编译阶段,会用函数体替换函数调用。缺陷:可能会使目标文件变大,优点:少了调用开销,提高程序运行效率。
inline对编译器而言只是建议,不同的编译器关于inline的实现机制可能不同,一般建议:将函数规模小的(即函数不是很长,具体没有准确的说法,取决于编译器内部实现)、不是递归、且频繁调用的函数采用inline修饰,否则编译器会忽略inline特性。
inline建议函数声明和定义不能分离,因为内联函数在预处理阶段就直接展开,因此内联函数不会进符号表,因此如果声明和定义分离,头文件只有声明,在预处理阶段,头文件展开,只知道该函数是一个内联函数,没有对应函数的定义,因此就无法完成替换,那就只能等通过call在链接阶段去找该函数,但是它是内联函数,没有进符号表,所以链接阶段就会报错。
🪆为什么是函数规模小?
假设一个函数经过编译,得到五十条汇编指令。普通情况下,调用此函数只需要一条call指令,调用10000此也就10000条call指令,但是如果把这个函数设置成内联函数,指令的数量就会大大增加,因为内联函数完成的是替换,把所有调用它的地方,都用函数体去替换,这也就意味着,原来1条call指令就能完成的任务,现在替换后就变成了50条指令,假如还是调用了10000次该函数,那就从10000条call指令,变成了500000条指令,其实这就是代码膨胀。
inline int Add(int x, int y) { cout << "xxxxxxxxxxxx" << endl; cout << "xxxxxxxxxxxx" << endl; cout << "xxxxxxxxxxxx" << endl; cout << "xxxxxxxxxxxx" << endl; cout << "xxxxxxxxxxxx" << endl; cout << "xxxxxxxxxxxx" << endl; cout << "xxxxxxxxxxxx" << endl; cout << "xxxxxxxxxxxx" << endl; cout << "xxxxxxxxxxxx" << endl; cout << "xxxxxxxxxxxx" << endl; cout << "xxxxxxxxxxxx" << endl; cout << "xxxxxxxxxxxx" << endl; return x + y ; } int main() { int ret = 0; ret = Add(3, 5); cout << ret << endl; return 0; }
对于上面函数体比较长的函数,即使我们人为规定了它是内联,但最终还是通过call指令去调用函数。
🪆为什么是被频繁调用?
因为普通函数在调用的时候会创建函数栈帧,若频繁调用就会频繁的创建栈帧,增加消耗。宏和内联,就是为了解决开销问题。如果调用的次数不多,开辟一点栈帧是无所谓的。
二、auto关键字
2.1 简介
C++11中规定:auto不再是一个存储类型指示符,而是作为一个新的类型指示符来指示编译器,auto声明的变量必须由编译器在编译时期推导而得。简单来说,auto会根据表达式自动推导类型。
int main() { int a = 0; auto b = a;//自动推导出b的类型是int auto c = 1.11 + 1;//自动推导出c的类型是double cout << typeid(b).name() << endl;//typeid可用来查看变量类型 cout << typeid(c).name() << endl; return 0; }
🪆注意:
使用auto定义变量时必须对其进行初始化,在编译阶段编译器需要根据初始化表达式,来推导auto的实际类型。因此,auto并非是一种“类型”的声明,而是一个类型声明的“占位符”,编译器在编译阶段会将auto替换为变量实际的类型。
int main() { auto a;//错误,必须要初始化 return 0; }








