条款30. 透彻了解inline的内外
此处注意,在C++98前,inline
关键字多用于提示编译器在调用该函数的地方用函数本体替换之,减少调用的消耗。现代编译器几乎不再参考coder手动写的inline
,而是通过编译器内部实现的统计方法自动inline
,因此对比原文的条款,更应注重inline
的其余用途.
此外:
模板函数和类内定义函数默认是inline的
函数指针call的函数以及虚函数不会被内联
.h文件中跨编译单元
- inline关键字允许一个函数在多个编译单元中重复存在,因此可以写在头文件中。
- inline关键字修饰的函数不保证一定会生成可链接的代码,因此必须写在头文件中,否则只能被当前编译单元使用,无法被其它编译单元调用。
如果在xxx.h
文件中声明并定义了函数,如果该头文件被多个其他文件包含的话,必须使用inline
关键字修饰,否则会报错multiple defined
.
那么inline函数究竟有什么作用呢?inline函数要放在 .h
头文件里边,非inline函数要放进 .cpp
源文件中,这就是最主要的区别。(.cpp
文件只被编译一次,.h
文件会被多次编译)
例如,有函数:
//a.h
#ifndef A_H
#define A_H
namespace staticTest {
inline int &getStaticValue() { // 此处不加inline的话,就会报错multiple defined
static int a = 1;
return a;
}
}
#endif //A_H
// f1.cpp
#include "iostream"
#include "a.h"
int f1() {
auto &v = staticTest::getStaticValue();
std::cout << &v << std::endl;
return ++v;
}
/
// f2.cpp
#include "iostream"
#include "a.h"
int f2() {
auto &v = staticTest::getStaticValue();
std::cout << &v << std::endl;
return ++v;
}
/
// main.cpp
#include <iostream>
#include "a.h"
using namespace std;
int f1();
int f2();
int main() {
int v1 = f1();
int v2 = f2();
cout << v1 << " " << v2 << " " << v1 + v2 << endl; // 2 3 5
}
C++17:作用于变量
可以在类内初始化static变量
//a.h
#ifndef A_H
#define A_H
struct Test{
inline static int value = 1;
};
#endif //A_H
// f1.cpp
#include "iostream"
#include "a.h"
int f1() {
auto v = Test::value;
std::cout << v << " " << &v << std::endl;
}
/
// f2.cpp
#include "iostream"
#include "a.h"
int f2() {
auto v = Test::value;
std::cout << v << " " << &v << std::endl;
}
/
// main.cpp
#include <iostream>
#include "a.h"
using namespace std;
int f1();
int f2();
int main() {
f1();
f2();
// 1 0xf5a43ff60c
// 1 0xf5a43ff60c
}
作用于namespace
可以在外部命名空间直接使用内部内容
// a.h
namespace Test {
int f1() {
std::cout << "f1" << std::endl;
}
inline namespace Test_c1 {
int f1(int i) {
std::cout << "child_f1 " << i << std::endl;
}
}
}
// main.cpp
int main() {
Test::f1(1); // 如果没有使用inline修饰namespace的话,只能Test::Test_c1::f1(1);
}