1、内联函数的定义
在类的内部声明或者定义的成员函数叫做内联函数,使用inline修饰的外部函数也可以作为内联函数。
何为内联函数(inline) | |
---|---|
(1)在类的内部声明或者定义的成员函数【成员函数的定义体可以放在类中也可以放在类外】 | |
(2)全局函数【普通类外函数】使用inline关键字修饰 |
举例:【这里仅是举例,后面会有详细的内联函数使用方法】
#include <iostream>
#include <string.h>
using namespace std;
class Person {
public:
//成员函数---->就是内联函数
void SetAge(int age)
{
Age = age;
}
int accessAge(void)
{
return Age;
}
//类内声明---->内联函数
inline void function(void);
private:
int Age;
};
//添加inline关键字的普通函数---->内联函数
inline void fun()
{
cout << "fun" << endl;
}
//类外定义
void Person::function(void)
{
cout << "function" << endl;
}
int main()
{
fun();
class Person xiaoming;
xiaoming.SetAge(18);
xiaoming.function();
cout << "年龄为:" << xiaoming.accessAge() << endl;
return 0;
}
运行结果:
fun
function
年龄为:18
2、内联函数的特性
内联函数主要的特性就是与其他普通函数相比,函数调用时存在不同。一般函数调用时,需要将程序的执行权转到被调用的函数,但是内联函数调用时,是将调用表达式使用函数体来代替。
总结一句话:函数调用成为函数体代码。
通俗来说:将内联函数中的代码放到主函数中。
说话不如画图来理解:
3、内联函数的处理过程
1)C++编译器在遇到调用内联函数的地方用函数体的代码来替换到函数调用。
好处:就是可以节省函数调用带来的参数传递、栈空间的进栈和压栈带来的开销,从而提高执行速度;
坏处:是增加了代码长度。
2)在linux环境下,可以通过一下指令来查看内联函数与普通函数在编译过程中的区别
g++ -S -O2 xxx.c -o xxx.s
其中的-O2是优化级别
优化类别 | 内容 |
---|---|
-O1 | 提供基础级别的优化 |
-O2 | 提供更加高级的代码优化,会占用更长的编译时间 |
-O3 | 提供了最高的代码优化 |
3)举例:
#include <iostream>
#include <string.h>
using namespace std;
//添加inline关键字的普通函数---->内联函数
inline void fun()
{
cout << "fun" << endl;
}
int main()
{
fun();
return 0;
}
在linux命令行下,执行
g++ -S -O2 xxx.c -o xxx.s
(1)内联函数编译结果:【尤其注意主函数】
.file "1.c"
.section .text.unlikely._ZNKSt5ctypeIcE8do_widenEc,"axG",@progbits,_ZNKSt5ctypeIcE8do_widenEc,comdat
.align 2
.LCOLDB0:
.section .text._ZNKSt5ctypeIcE8do_widenEc,"axG",@progbits,_ZNKSt5ctypeIcE8do_widenEc,comdat
.LHOTB0:
.align 2
.p2align 4,,15
.weak _ZNKSt5ctypeIcE8do_widenEc
.type _ZNKSt5ctypeIcE8do_widenEc, @function
_ZNKSt5ctypeIcE8do_widenEc:
.LFB824:
.cfi_startproc
movzbl 8(%esp), %eax
ret
.cfi_endproc
.LFE824:
.size _ZNKSt5ctypeIcE8do_widenEc, .-_ZNKSt5ctypeIcE8do_widenEc
.section .text.unlikely._ZNKSt5ctypeIcE8do_widenEc,"axG",@progbits,_ZNKSt5ctypeIcE8do_widenEc,comdat
.LCOLDE0:
.section .text._ZNKSt5ctypeIcE8do_widenEc,"axG",@progbits,_ZNKSt5ctypeIcE8do_widenEc,comdat
.LHOTE0:
.section .rodata.str1.1,"aMS",@progbits,1
.LC1:
.string "fun"
.section .text.unlikely,"ax",@progbits
.LCOLDB2:
.section .text.startup,"ax",@progbits
.LHOTB2:
.p2align 4,,15
.globl main
.type main, @function
main: //主函数汇编结果117行
.LFB1076:
.cfi_startproc
leal 4(%esp), %ecx
.cfi_def_cfa 1, 0
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
.cfi_escape 0x10,0x5,0x2,0x75,0
movl %esp, %ebp
pushl %ebx
pushl %ecx
.cfi_escape 0xf,0x3,0x75,0x78,0x6
.cfi_escape 0x10,0x3,0x2,0x75,0x7c
subl $4, %esp
pushl $3
pushl $.LC1
pushl $_ZSt4cout
call _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_i
movl _ZSt4cout, %eax
addl $16, %esp
movl -12(%eax), %eax
movl _ZSt4cout+124(%eax), %ebx
testl %ebx, %ebx
je .L10
cmpb $0, 28(%ebx)
je .L4
movzbl 39(%ebx), %eax
.L5:
subl $8, %esp
movsbl %al, %eax
pushl %eax
pushl $_ZSt4cout
call _ZNSo3putEc
movl %eax, (%esp)
call _ZNSo5flushEv
addl $16, %esp
leal -8(%ebp), %esp
xorl %eax, %eax
popl %ecx
.cfi_remember_state
.cfi_restore 1
.cfi_def_cfa 1, 0
popl %ebx
.cfi_restore 3
popl %ebp
.cfi_restore 5
leal -4(%ecx), %esp
.cfi_def_cfa 4, 4
ret
.L4:
.cfi_restore_state
subl $12, %esp
pushl %ebx
call _ZNKSt5ctypeIcE13_M_widen_initEv
movl (%ebx), %eax
addl $16, %esp
movl 24(%eax), %edx
movl $10, %eax
cmpl $_ZNKSt5ctypeIcE8do_widenEc, %edx
je .L5
pushl %eax
pushl %eax
pushl $10
pushl %ebx
call *%edx
addl $16, %esp
jmp .L5
.L10:
call _ZSt16__throw_bad_castv
.cfi_endproc
.LFE1076:
.size main, .-main
.section .text.unlikely
.LCOLDE2:
.section .text.startup
.LHOTE2:
.section .text.unlikely
.LCOLDB3:
.section .text.startup
.LHOTB3:
.p2align 4,,15
.type _GLOBAL__sub_I_main, @function
_GLOBAL__sub_I_main:
.LFB1086:
.cfi_startproc
subl $24, %esp
.cfi_def_cfa_offset 28
pushl $_ZStL8__ioinit
.cfi_def_cfa_offset 32
call _ZNSt8ios_base4InitC1Ev
addl $12, %esp
.cfi_def_cfa_offset 20
pushl $__dso_handle
.cfi_def_cfa_offset 24
pushl $_ZStL8__ioinit
.cfi_def_cfa_offset 28
pushl $_ZNSt8ios_base4InitD1Ev
.cfi_def_cfa_offset 32
call __cxa_atexit
addl $28, %esp
.cfi_def_cfa_offset 4
ret
.cfi_endproc
.LFE1086:
.size _GLOBAL__sub_I_main, .-_GLOBAL__sub_I_main
.section .text.unlikely
.LCOLDE3:
.section .text.startup
.LHOTE3:
.section .init_array,"aw"
.align 4
.long _GLOBAL__sub_I_main
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.hidden __dso_handle
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits
(2)将fun函数改为普通函数(将inline关键字去掉):【尤其注意主函数】
.file "1.c"
.section .text.unlikely._ZNKSt5ctypeIcE8do_widenEc,"axG",@progbits,_ZNKSt5ctypeIcE8do_widenEc,comdat
.align 2
.LCOLDB0:
.section .text._ZNKSt5ctypeIcE8do_widenEc,"axG",@progbits,_ZNKSt5ctypeIcE8do_widenEc,comdat
.LHOTB0:
.align 2
.p2align 4,,15
.weak _ZNKSt5ctypeIcE8do_widenEc
.type _ZNKSt5ctypeIcE8do_widenEc, @function
_ZNKSt5ctypeIcE8do_widenEc:
.LFB824:
.cfi_startproc
movzbl 8(%esp), %eax
ret
.cfi_endproc
.LFE824:
.size _ZNKSt5ctypeIcE8do_widenEc, .-_ZNKSt5ctypeIcE8do_widenEc
.section .text.unlikely._ZNKSt5ctypeIcE8do_widenEc,"axG",@progbits,_ZNKSt5ctypeIcE8do_widenEc,comdat
.LCOLDE0:
.section .text._ZNKSt5ctypeIcE8do_widenEc,"axG",@progbits,_ZNKSt5ctypeIcE8do_widenEc,comdat
.LHOTE0:
.section .rodata.str1.1,"aMS",@progbits,1
.LC1:
.string "fun"
.section .text.unlikely,"ax",@progbits
.LCOLDB2:
.text
.LHOTB2:
.p2align 4,,15
.globl _Z3funv
.type _Z3funv, @function
_Z3funv:
.LFB1075:
.cfi_startproc
pushl %ebx
.cfi_def_cfa_offset 8
.cfi_offset 3, -8
subl $12, %esp
.cfi_def_cfa_offset 20
pushl $3
.cfi_def_cfa_offset 24
pushl $.LC1
.cfi_def_cfa_offset 28
pushl $_ZSt4cout
.cfi_def_cfa_offset 32
call _ZSt16__ostream_insertIcSt11char_traitsIcEERSt13basic_ostreamIT_T0_ES6_PKS3_i
movl _ZSt4cout, %eax
addl $16, %esp
.cfi_def_cfa_offset 16
movl -12(%eax), %eax
movl _ZSt4cout+124(%eax), %ebx
testl %ebx, %ebx
je .L10
cmpb $0, 28(%ebx)
je .L4
movsbl 39(%ebx), %eax
.L5:
subl $8, %esp
.cfi_def_cfa_offset 24
pushl %eax
.cfi_def_cfa_offset 28
pushl $_ZSt4cout
.cfi_def_cfa_offset 32
call _ZNSo3putEc
movl %eax, (%esp)
call _ZNSo5flushEv
addl $24, %esp
.cfi_def_cfa_offset 8
popl %ebx
.cfi_restore 3
.cfi_def_cfa_offset 4
ret
.p2align 4,,10
.p2align 3
.L4:
.cfi_def_cfa_offset 16
.cfi_offset 3, -8
subl $12, %esp
.cfi_def_cfa_offset 28
pushl %ebx
.cfi_def_cfa_offset 32
call _ZNKSt5ctypeIcE13_M_widen_initEv
movl (%ebx), %eax
addl $16, %esp
.cfi_def_cfa_offset 16
movl 24(%eax), %edx
movl $10, %eax
cmpl $_ZNKSt5ctypeIcE8do_widenEc, %edx
je .L5
subl $8, %esp
.cfi_def_cfa_offset 24
pushl $10
.cfi_def_cfa_offset 28
pushl %ebx
.cfi_def_cfa_offset 32
call *%edx
addl $16, %esp
.cfi_def_cfa_offset 16
movsbl %al, %eax
jmp .L5
.L10:
call _ZSt16__throw_bad_castv
.cfi_endproc
.LFE1075:
.size _Z3funv, .-_Z3funv
.section .text.unlikely
.LCOLDE2:
.text
.LHOTE2:
.section .text.unlikely
.LCOLDB3:
.section .text.startup,"ax",@progbits
.LHOTB3:
.p2align 4,,15
.globl main
.type main, @function
main: //主函数汇编结果71行
.LFB1076:
.cfi_startproc
leal 4(%esp), %ecx
.cfi_def_cfa 1, 0
andl $-16, %esp
pushl -4(%ecx)
pushl %ebp
.cfi_escape 0x10,0x5,0x2,0x75,0
movl %esp, %ebp
pushl %ecx
.cfi_escape 0xf,0x3,0x75,0x7c,0x6
subl $4, %esp
call _Z3funv
addl $4, %esp
xorl %eax, %eax
popl %ecx
.cfi_restore 1
.cfi_def_cfa 1, 0
popl %ebp
.cfi_restore 5
leal -4(%ecx), %esp
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE1076:
.size main, .-main
.section .text.unlikely
.LCOLDE3:
.section .text.startup
.LHOTE3:
.section .text.unlikely
.LCOLDB4:
.section .text.startup
.LHOTB4:
.p2align 4,,15
.type _GLOBAL__sub_I__Z3funv, @function
_GLOBAL__sub_I__Z3funv:
.LFB1086:
.cfi_startproc
subl $24, %esp
.cfi_def_cfa_offset 28
pushl $_ZStL8__ioinit
.cfi_def_cfa_offset 32
call _ZNSt8ios_base4InitC1Ev
addl $12, %esp
.cfi_def_cfa_offset 20
pushl $__dso_handle
.cfi_def_cfa_offset 24
pushl $_ZStL8__ioinit
.cfi_def_cfa_offset 28
pushl $_ZNSt8ios_base4InitD1Ev
.cfi_def_cfa_offset 32
call __cxa_atexit
addl $28, %esp
.cfi_def_cfa_offset 4
ret
.cfi_endproc
.LFE1086:
.size _GLOBAL__sub_I__Z3funv, .-_GLOBAL__sub_I__Z3funv
.section .text.unlikely
.LCOLDE4:
.section .text.startup
.LHOTE4:
.section .init_array,"aw"
.align 4
.long _GLOBAL__sub_I__Z3funv
.local _ZStL8__ioinit
.comm _ZStL8__ioinit,1,1
.hidden __dso_handle
.ident "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.12) 5.4.0 20160609"
.section .note.GNU-stack,"",@progbits
4、类中内联函数的三种使用方式
1)隐式内联
#include <iostream>
using namespace std;
class Person {
public:
void fun()
{
cout << "这是隐式内联!!!" << endl;
}
};
int main()
{
class Person people;
people.fun();
return 0;
}
运行结果:
这是隐式内联!!!
2)在类内部显示声明,在类外部定义
#include <iostream>
using namespace std;
class Person {
public:
//类内显示声明
inline void fun(void);
};
//类外部定义
void Person::fun(void)
{
cout << "类内部显示声明,类外部定义!!!" << endl;
}
int main()
{
class Person people;
people.fun();
return 0;
}
运行结果:
类内部显示声明,类外部定义!!!
3)追加内联:在类内部没有显示声明,在类外部显示定义
#include <iostream>
using namespace std;
class Person {
public:
//类内部没有显示声明
void fun(void);
};
//类外部显示定义
inline void Person::fun(void)
{
cout << "类内部没有显示声明,类外部显示定义!!!" << endl;
}
int main()
{
class Person people;
people.fun();
return 0;
}
运行结果:
类内部没有显示声明,类外部显示定义!!!
5、内联的使用要求
1)内联函数不一定都会被展开,【inline只是给编译器一个建议】,至于编译器要不用采纳另当别论;
2)通过仅仅把代码行数不超过5行的函数作为内联函数,如果内联函数中的代码数量过多时,编译器会把它看作一个普通函数调用;【划重点了,内联函数一般比较简单】
3)在内联函数中,不允许使用循环语句(while,for)和开关语句(switch),否则将会视为普通函数;【循环、开关函数不可用于内联】
4)递归函数不能作为内联函数。【递归也不行】
6、内联函数与宏定义的区别
1)调用时机不同:内联函数在函数执行时会被调用,宏定义在预处理阶段就被调用;
2)编译器会对内联函数的参数类型做安全检验和自动类型转换,而宏定义不会;
3)内联函数可以访问类的成员属性,而宏定义不能;
4)在类内声明同时定义的成员函数自动转化为内联函数。