什么时候适合在 C++ 中使用静态(通过未命名的命名空间)?
Posted
技术标签:
【中文标题】什么时候适合在 C++ 中使用静态(通过未命名的命名空间)?【英文标题】:When is it appropriate to use static (over unnamed namespaces) in C++? 【发布时间】:2011-04-22 22:09:25 【问题描述】:我整天都在阅读有关未命名命名空间的文章,大多数文章都解释了何时应该使用未命名命名空间而不是 static 关键字。但是我仍然有一个大问题,什么时候使用静态合适?毕竟它还没有完全被弃用,那么我现在应该把带有静态函数的头文件放到未命名的命名空间中吗?
#ifndef HEADER_H
#define HEADER_H
static int func()
...
// versus:
namespace
int func()
...
;
#endif // HEADER_H
或者静态成员函数呢?
问候
【问题讨论】:
将静态(或未命名的命名空间)放在头文件中似乎适得其反。如果它们是静态的,为什么它们不只是在源文件中? 【参考方案1】:标准的准确措辞是:
在命名空间范围内声明对象时,不推荐使用
static
关键字。
头文件中的函数应该是 inline
而不是 static
或在未命名的命名空间中。 inline
意味着您在程序中最多只能得到一个函数副本,而其他方法将为您提供每个包含标题的文件的单独副本。除了膨胀之外,如果函数包含函数静态数据,这可能会产生不正确的行为。 (编辑: 除非函数应该在不同的编译单元中有不同的定义,可能是由于在包含头文件之前定义了不同的预处理器宏。在这种情况下,最好的方法是不包含它根本,而是把它埋在一个没有标记的坟墓里,用一根木桩穿过它邪恶的心脏。)
数据对象,除了常量,通常根本不应该在头文件中定义,只声明extern
。
静态成员函数是另一回事,你必须在那里使用static
,因为没有其他方法可以声明它们。这种用法并未被弃用,因为它不在命名空间范围内。
更新: C++11 已删除弃用,因此不再有任何特殊理由更喜欢未命名的命名空间而不是 static
。但是你仍然不应该在头文件中使用任何一个,除非你正在做一些奇怪的事情。
【讨论】:
"数据对象根本不应该在头文件中定义,只有在需要外部链接时才声明为 extern。"这是不精确的。正如您所说的,“数据对象”默认情况下具有外部链接,除非它们是 const。所以需要 extern 来使他们的声明只是一个声明而不是一个定义,这是默认的。但是,如果 extern 和初始化程序都存在,则它是一个定义。 数据对象通常需要在头文件中定义。这包括常量(它们是对象)。我想对于 C++ 新手来说,避免在头文件中定义非整数类型的(静态类成员)常量是一个很好的建议。更有经验的程序员可以使用例如模板化常量技巧。另一个这样的技巧是返回对局部静态常量的引用的函数。 @Armen, @Alf:是的,我对数据对象的建议有点简单。我会改写它。 好的,很好地解释了外部和静态成员函数。关于内联,我一直认为它会用函数代码本身替换函数调用,因此我最终会得到很多副本?! "或在一个未命名的命名空间中。" - 不,那太糟糕了。未命名的命名空间适用于想要使数据、函数或类型 TU 本地化的 .cpp 文件。这样他们就可以使用通用名称并保证不会发生冲突。但是如果你把它们放在标题中,你会污染那个私有环境!【参考方案2】:static
如果您使用某些工具,可能会更好。在大多数文本编辑器中,它的自动缩进功能也表现得更好。有点难过,当你不得不避免使用有用的东西时,因为它不能与真正支持它的工具一起工作,但我保证你会克服它。
一个给调试部门带来一些潜在痛苦暗示的示例问题:
Viewing namespaced global variables in Visual Studio debugger?
您可能不必费力寻找更多的问题,但调试器问题足以让我尽最大可能完全放弃命名空间,所以我再也没有进一步研究。
我个人的建议是,对于不会永远存在的代码,不妨使用static
。与未命名的命名空间有效地做同样的事情,但是,平均所有工具,它得到了更好的支持。理论上,有一天它会完全消失,但我很高兴公开承认,我确信那一天永远不会真正到来。同时,您也为自己省去了一些痛苦。
【讨论】:
【参考方案3】:在头文件中,指定内部链接或使用匿名命名空间通常没有意义。
在单独编译的实现文件中,您可以使用static
或匿名命名空间来避免链接级别名称冲突或依赖于实现细节的客户端代码。匿名命名空间让您拥有外部链接,这是模板参数所必需的,它还支持类定义。但归根结底,这只是实用性和个人喜好的问题,视具体情况而定。
static
的成员函数与链接规范无关。 static
成员函数无论如何都有外部链接。
【讨论】:
【参考方案4】:我知道,命名空间范围内的静态与未命名的命名空间相比没有任何优势。使用未命名的命名空间,如上例所示。实际上,在上面的示例中,我看不出为什么需要静态或未命名的命名空间。也许内联? 静态成员函数与命名空间范围内的静态无关。静态成员函数(和非函数成员)仍然有效。
【讨论】:
在示例中需要静态以避免函数具有外部链接。由于它将在每个使用标头的模块中实现,因此如果 static 关键字不存在,链接器可能会反对。 @Alexander:好吧,内联将允许函数的多个定义。在我看来,这是一个更好的选择。但是,如果“内部链接”正是需要的,那么未命名的命名空间会更好,至少因为它没有被弃用。inline
允许函数的多个相同定义。 static
允许多个不同的定义,因为每个翻译单元都有自己的内部链接功能,它们恰好被同名引用。因此,如果func
的定义中有某些内容依赖于某些#defines(例如assert
),并且您希望在#define 中组合包含不同值的标头的不同TU 是合法的,那么您需要 static
或未命名的命名空间。 inline
不会做,虽然在实践中它可能会起作用。
如果内部链接 正是所需要的,那么一个未命名的命名空间是不够的。虽然这可能是技术问题,但默认情况下,未命名命名空间(除了其他命名空间)的成员具有外部链接。
我的 FCD 说“虽然未命名命名空间中的实体可能具有外部链接,但它们实际上是由其翻译单元唯一的名称限定的,因此永远无法从任何其他翻译单元看到。”跨度>
以上是关于什么时候适合在 C++ 中使用静态(通过未命名的命名空间)?的主要内容,如果未能解决你的问题,请参考以下文章
C++函数通过头文件分享给其他源文件时,使用命名空间方式,和使用类中静态函数方式,有何区别?