为什么 C + + 内联函数在头部?

这不是一个关于如何使用内联函数或者它们如何工作的问题,更多的是为什么它们是这样做的。

类成员函数的声明不需要将函数定义为 inline,它只是函数的实际实现。例如,在头文件中:

struct foo{
void bar(); // no need to define this as inline
}

那么为什么类函数 的内联实现要放在头文件中呢?为什么我不能把内联函数的 .cpp文件?如果我尝试将内联定义放在 .cpp文件中,我会得到如下错误:

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
142199 次浏览

因为编译器需要看到它们以便 内嵌式它们。头文件是通常包含在其他翻译单元中的“组件”。

#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 + + 编译器的一个限制。如果将函数放在头部,所有可以内联的 cpp 文件都可以看到函数的“源”,内联可以由编译器完成。否则内联将必须由链接器完成(每个 cpp 文件分别在 obj 文件中编译)。问题是在连接器中做这件事要困难得多。“模板”类/函数也存在类似的问题。它们需要由编译器实例化,因为链接器在实例化(创建特定版本)它们时会遇到问题。一些较新的编译器/链接器可以执行“两步”编译/链接,编译器执行第一步,然后链接器执行其工作并调用编译器来解决未解决的问题(内联/模板...)

inline函数的定义不一定要在头文件中,但是,由于内联函数的 一个定义规则(< a href = “ https://en.cpferences ence.com/w/cpp/language/Definition”rel = “ noReferrer”> ODR ),函数的相同定义必须存在于使用它的每个转换单元中。

实现这一点的最简单方法是将定义放在一个头文件中。

如果你想把一个函数的定义放在一个源文件中,那么你不应该声明它为 inline。未声明 inline的函数并不意味着编译器不能内联该函数。

你是否应该声明一个函数 inline通常是一个选择,你应该根据哪个版本的 一个定义规则对你来说是最有意义的遵循; 添加 inline然后被后续的约束限制是没有意义的。

原因是编译器必须实际看到 < em > 定义 ,以便能够将它放在调用的位置。

请记住,C 和 C + + 使用一个非常简单的编译模型,其中编译器一次只能看到一个翻译单元。(这对于导出来说是失败的,这是只有一个供应商实际实现它的主要原因。)

C + + inline关键字具有误导性,它并不意味着“内联此函数”。如果一个函数被定义为内联的,它只是意味着只要所有定义都相等,就可以多次定义它。对于标记为 inline的函数来说,它是一个被调用的实际函数,而不是在它被调用的地方内联代码,这是完全合法的。

模板需要在头文件中定义一个函数,因为例如,一个模板化的类实际上不是一个类,它是一个类的 模板,您可以对它进行多种变化。为了让编译器能够创建一个 Foo<int>::bar()函数 当您使用 Foo 模板创建 Foo 类时Foo<T>::bar()的实际定义必须是可见的。

有两种看法:

  1. 内联函数是在标头中定义的,因为为了内联函数调用,编译器必须能够看到函数体。对于初级编译器来说,函数体必须与调用位于同一个转换单元中。(现代编译器可以跨越翻译单元进行优化,因此即使函数定义位于单独的翻译单元中,函数调用也可以是内联的,但是这些优化代价高昂,并不总是启用,也不总是得到编译器的支持)

  2. 在头中定义的函数必须标记为 inline,否则,包含头的每个翻译单元将包含函数的定义,链接器将抱怨多个定义(违反了一个定义规则)。inline关键字抑制了这一点,允许多个翻译单元包含(相同的)定义。

这两种解释实际上归结为这样一个事实: inline关键字并不完全符合您的预期。

C + + 编译器可以随时应用内联 优化(用被调用函数的主体替换函数调用,节省调用开销) ,只要它不改变程序的可观察行为。

通过允许函数定义在多个翻译单元中可见,inline关键字使编译器应用这种优化成为 更容易,但使用这个关键字并不意味着编译器 已经要内联函数,而使用这个关键字的 没有并不禁止编译器内联函数。

我知道这是一个老线程,但认为我应该提到的 extern关键字。我最近碰到了这个问题,解决方法如下

救命啊

namespace DX
{
extern inline void ThrowIfFailed(HRESULT hr);
}

Helper.cpp

namespace DX
{
inline void ThrowIfFailed(HRESULT hr)
{
if (FAILED(hr))
{
std::stringstream ss;
ss << "#" << hr;
throw std::exception(ss.str().c_str());
}
}
}

内联函数

在 C + + 中,宏只是一个内联函数,所以宏现在处于编译器的控制之下。

  • 重点 : 如果我们在类中定义一个函数,它将自动变成 插进去

内联函数的代码在被调用的地方被替换,从而减少了调用函数的开销。

在某些情况下,函数内联不能工作,例如

  • 如果在内联函数中使用静态变量。

  • 如果函数很复杂。

  • 如果函数的递归调用

  • 如果函数地址被隐式或显式采用

在类外定义的如下函数可能成为内联函数

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

类内定义的函数也变成了内联函数

// 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 函数都将成为内联函数