在头文件中实现的函数的static与inline

static vs inline for functions implemented in header files

本文关键字:函数 static inline 实现 文件      更新时间:2023-10-16

我认为C++中的inline用于链接/作用域。我把它和全局对象的externstatic放在同一个篮子里。

通常,对于在头文件中实现的函数,我的首选解决方案是使其静态:

// In Foo.h
static void foo()
{
// Do stuff...
}

然而,我相信这也是有效的,似乎没有违反ODR:

// In Foo.h
inline void foo()
{
// Do stuff...
}

两者在语义上有什么区别?此外,我也不确定C++标准的哪些领域可以解释确切的差异,或者它是否只是未定义的,差异在于实现。

inline准确地传达了您想要的内容:"请取消此函数的ODR(一个定义规则),以便每个翻译单元可以(也必须)提供自己的函数定义副本"。

然后,编译器将内联调用该函数,或者将来自不同TU的函数定义合并在一起(以便生成的函数在可执行文件中存在一次)。

另一方面,static告诉编译器在定义该函数的每个翻译单元中生成该函数,而不是共享该函数。因此,最终可执行文件中存在任意数量的技术上独立的函数。

简而言之,如果您使用static,那么在不同的转换单元中获取函数的地址将返回不同的地址(因为您告诉编译器在每个TU中生成一个函数),但如果您使用inline,它们将显示相同的地址(由于您定义了一个函数,并只是告诉编译器将许多定义合并在一起)。

主要区别在于函数中的任何静态局部变量会发生什么——如果函数是static,那么每个编译单元都将有自己的静态局部变量副本,这与任何其他编译单元都不同。如果函数是inline,那么所有编译单元将只共享一个(一组)静态本地。

在许多情况下,您不会注意到差异,因为编译器和链接器现在非常聪明。但是,内联函数的行为必须像它是一个正则函数一样。头中的静态函数将被编译到包含它的每个源文件中,因此会有很多它的副本

大多数情况下,这并不重要,但也有一些方法可以做到。内联函数有一个地址。静态函数在每个翻译单元中都有不同的地址。

静态局部变量:在内联中,将有它们的单个副本。对于静态函数,对于包含该函数的每个翻译单元,每个静态局部变量都将有一个唯一的副本。

从关键字inlinestatic的原意来思考这个问题可能会更有帮助,也更清晰。

  1. 内联函数

关键字inline的初衷是提高运行时性能,而不是像您一开始所说的那样用于链接/作用域。这是一个提示,使编译器尝试在调用点内联生成代码,而不是一次编写代码并每次调用它,这样可以避免一些开销,例如为调用创建堆栈帧。为了生成内联代码,函数定义必须在范围中,而不仅仅是像普通函数那样的声明。因此,您应该将整个函数定义放在一个头文件foo.h中,调用它时放在#include "foo.h"中。这些位于不同翻译单元中的inline函数必须逐个令牌相同,才能遵守ODR(一个定义规则)。所有的inline函数都是一个函数,这个inline函数中的static变量也是如此。

  1. 静态函数

关键字static可用于使函数成为翻译单元的本地函数,也就是说,它赋予它们内部链接。因此,如果将foo()的整个函数定义放入头文件foo.h中,并将其标记为static,则#include "foo.h"的所有翻译单元都将具有本地函数foo()。换句话说,不同翻译单元中的函数foo()不是一个单一的函数,这些static函数中的static变量也不是。

  1. 静态内联函数

因此您可以猜测由staticinline标记的函数。这些函数不像static函数那样在不同的翻译单元中是相同的,但可以通过内联生成代码来提高性能。

似乎没有人提到在C++中,静态函数是直接调用的函数,而不是在类的实例上调用的函数。换句话说,不存在隐含的"this"指针。如果类MyClass的函数foo是静态的,那么您说:

MyClass::foo();//称之为

而不是:MyClass an_object=新建MyClass();an_object->foo();