Why are C++ inline functions in the header?
nb这不是一个关于如何使用内联函数或它们是如何工作的问题,而是为什么它们是按照它们的方式完成的。
类成员函数的声明不需要将函数定义为
1 2 3 | struct foo{ void bar(); // no need to define this as inline } |
那么,为什么类函数的内联实现必须在头文件中呢?为什么不能将内联函数放入
1 2 3 4 5 | error LNK2019: unresolved external symbol "public: void __thiscall foo::bar(void)" (?bar@foo@@QAEXXZ) referenced in function _main 1>C:\Users\Me\Documents\Visual Studio 2012\Projects\inline\Debug\inline.exe : fatal error LNK1120: 1 unresolved externals |
实现这一点的最简单方法是将定义放在头文件中。
如果要将函数定义放在单个源文件中,则不应将其声明为
无论您是否应该声明一个函数
有两种方法来看待它:
内联函数在头中声明,因为为了内联函数调用,编译器必须能够看到函数体。对于幼稚的编译器,函数体必须与调用位于同一翻译单元中。(现代编译器可以跨翻译单元进行优化,因此即使函数定义在单独的翻译单元中,函数调用也可能是内联的,但这些优化代价高昂,并不总是启用的,而且编译器并不总是支持这些优化)
头中声明的函数必须标记为
这两种解释实际上归结为一个事实,即
C++编译器可以自由地应用内联优化(用调用函数的函数替换函数调用,节省调用开销),只要它不改变程序的可观察行为。
通过允许函数定义在多个翻译单元中可见,
这是C++编译器的一个限制。如果将函数放在头文件中,所有可以内联的cpp文件都可以看到函数的"源文件",并且内联可以由编译器完成。否则,内联将必须由链接器完成(每个cpp文件分别编译在一个obj文件中)。问题是,在链接器中这样做要困难得多。"模板"类/函数也存在类似的问题。它们需要由编译器实例化,因为链接器在实例化(创建专用版本)它们时会遇到问题。一些新的编译器/链接器可以执行"两遍"编译/链接,其中编译器执行第一遍,然后链接器执行其工作并调用编译器来解决未解决的问题(内联/模板…)
C++ EDCOX1的0个关键字是误导性的,并不意味着"内联这个函数"。如果一个函数被定义为inline,那么它只意味着只要所有定义都相等,它就可以被定义多次。标记为
模板需要在头文件中定义一个函数,因为模板化的类实际上不是一个类,所以它是一个类的模板,您可以对其进行多种变体。为了使编译器能够在使用foo模板创建foo类时生成
原因是编译器必须实际看到定义,以便能够将其放在调用位置。
请记住,C和C++使用一个非常简单的编译模型,编译器总是一次只看到一个翻译单元。(这对于出口来说是失败的,这是只有一个供应商实际执行的主要原因。)
我知道这是一条老线索,但我认为我应该提到
帮助者
1 2 3 4 | namespace DX { extern inline void ThrowIfFailed(HRESULT hr); } |
Help.CPP
1 2 3 4 5 6 7 8 9 10 11 12 | namespace DX { inline void ThrowIfFailed(HRESULT hr) { if (FAILED(hr)) { std::stringstream ss; ss <<"#" << hr; throw std::exception(ss.str().c_str()); } } } |
因为编译器需要看到它们以便内联它们。头文件是通常包含在其他翻译单元中的"组件"。
1 2 3 | #include"file.h" // Ok, now me (the compiler) can see the definition of that inline function. // So I'm able to replace calls for the actual implementation. |
内联函数
在C++中,宏只不过是内联函数。所以现在宏由编译器控制。
- 重要提示:如果我们在类内定义一个函数,它将自动成为内联函数。
内联函数的代码在调用的地方被替换,从而减少了调用函数的开销。
在某些情况下,函数的内联无法工作,例如
如果在内联函数内使用静态变量。
如果函数很复杂。
如果函数的递归调用
如果函数的地址是含蓄的或明确的
下面在类外部定义的函数可能成为内联函数
1 2 3 | inline int AddTwoVar(int x,int y); //This may not become inline inline int AddTwoVar(int x,int y) { return x + y; } // This becomes inline |
类内部定义的函数也变为内联
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // Inline SpeedMeter functions class SpeedMeter { int speed; public: int getSpeed() const { return speed; } void setSpeed(int varSpeed) { speed = varSpeed; } }; int main() { SpeedMeter objSM; objSM.setSpeed(80); int speedValue = A.getSpeed(); } |
这里getspeed和setspeed函数都将成为内联函数