引子程序
1 | // file.cc |
上面的编码会出现什么问题? 链接错误,显示foo未定义(undefined reference to `foo()’).
编译器对inline函数的处理
使用inline函数,意图是在函数调用处直接替换成函数体,省去函数调用,提高函数的执行效率.但编译器在处理的时候,真正的处理流程是什么样的呢?
我们知道,c/c++程序编译分为几个阶段,预处理->编译+汇编->链接.inline的处理阶段发生在编译阶段.也就是说,在编译阶段完成后,目标模块的inline函数要么被替换了,要么没有被替换,但是链接时可以找到inline函数的定义.也就是说,在编译阶段之前,目标模块需要看到inline函数的实现.
拿引子程序来说,编译main.o目标模块时,编译器”看到”了foo的声明,此时它并不知道foo是要inline的.编译file.o目标模块时,编译器看到foo的实现,并且声明为inline,由于foo在file.o没有被使用到,所以,inline函数不被保留.链接的时候,当然也就找不到foo的定义实体了.针对此例子,我们可以在file.c定义一处函数,使用了一次foo(),编译就可以通过.1
2
3
4
5//file.cc
int foo2()
{
foo();
}
正确的做法是,inline函数的实现(定义)要跟随其函数声明放在.h文件中.编译器在编译阶段就可以看见函数实现,从而进行替换.(这一点有点向函数模板,函数模板也是要放在声明文件中,不能单独放在源文件中)
inline和宏的区别
- 1.宏define在预处理阶段完成;inline在编译阶段完成;
- 2.类型安全检查,inline是函数,需要做类型检查;
- 3.替换方式:宏是字符串拷贝替换,会出现边际效应;inline是嵌入代码,在编译过程中不单独产生代码。
- 4.宏不可调试,inline函数可以调试。这里的“调试”之意是指在程序的debug版本是没有内联的,编译器会生成像普通函数一样含有调试信息的可执行代码。在release版本,才实现真正的内联。
- 5.inline函数可以操作私有数据成员。
内联函数的使用
inline函数的编程风格
关键字inline必须与函数定义放在一起才能使函数真正内联,仅把inlilne放在函数声明的前面不起任何作用.1
2
3
4
5
6
7
8
9
10
11
12
13//Foo不能内联
inline void Foo(int x, int y);
void Foo(int x, int y)
{
...
}
//Foo内联
void Foo(int x, int y);
inline Foo(int x, int y)
{
...
}
inline是一种”用于实现的关键字”,而不是一种”用于声明的关键字”.
慎用内联
- 如果函数体内的代码过长,使用内联将导致可执行代码膨胀过大
- 如果函数体内的代码出现循环或者其他复杂的控制结构,那么执行函数体内代码的时间比函数调用的开销大得多,因此内联的意义不大.
不要轻易让构造函数和析构函数成为内联函数
inline仅仅是对编译器的一种请求,编译器可以无视
inline常跟static一起
当多个源文件包含同一个inline函数,但是编译器又无视inline请求,如此一来,在每个目标模块中都有该函数的实现实例,造成重定义错误。把inline函数同时声明为static函数(限制其作用域为源文件内部),可以解决问题。