C++ 命名空间函数

37

我是一名Java开发人员,对C++相当陌生。我需要实现某种实用类,并考虑将方法实现为静态的。然而,我遇到了一个有关命名空间函数与静态方法的stackoverflow问题,显然命名空间函数是首选方法。因此,我想知道是否有任何文章或示例介绍如何实现命名空间函数。例如,应该如何在头文件中声明命名空间函数?头文件是否只包含函数定义,就像类头文件一样,实现应该在cpp文件中,还是应该直接在头文件中实现函数?

基本上,我正在尝试实现一个应用程序来解析包含一些命令的文本文件。因此,我考虑实现静态辅助方法来处理文本处理。例如readCommand(string line)。请告诉我如果我走了错误的方向。谢谢。


1
C++对函数和类之间有很明显的区分。从你的问题中并不清楚你想使用哪一个。 - Shep
嗨Shep,感谢您的评论。我在我的问题中添加了更多细节。这是否回答了您的问题? - K Hein
3个回答

32
我应该如何在头文件中声明命名空间函数?
namespace MON {
// extern:
t_ret func(const t_param& pValue);
// 'inline':
inline t_ret inline_func(const t_param& pValue) { ... }
} // << MON
应该根据是否需要内联或导出函数来确定头文件中是否只包含函数定义,类似于类头文件和实现应该在cpp文件中,还是直接在头文件中实现函数。这通常取决于最小化依赖关系。
要详细说明导出或内联内容:
在C++中,您通常会选择使用外部函数来最小化依赖关系。这相当于将类方法的定义与声明分开: file.hpp
namespace MON {
// extern:
t_ret func(const t_param& pValue);
} // << MON

file.cpp

#include "hefty_stuff.hpp"

MON::t_ret MON::func(const t_param& pValue) { ... }

然而,在某些情况下,定义可见性是至关重要的,通常是为了提高性能或者当您知道大小很重要且标头没有包含在许多地方时。因此,inline变体也是一个选择。

内联函数仍可以被导出,并且可以按请求进行内联--但是,任何内联函数副本都可能会被合并(具体来说,实现可以假设所有定义都相等,任何函数的副本都是不必要的)。

有了导出的定义,您可以有选择地限制(或隔离)您的包含依赖项。也就是说,#include "hefty_stuff.hpp"不需要在头文件中使用file.hpp中的函数。


基本上,我正在尝试实现一个应用程序,以解析包含一些命令的文本文件。因此,我考虑实现静态帮助器方法来处理文本处理。

好的,这里应该避免使用static关键字。C++ 使用单一定义规则。使用static将仅导致许多不必要的副本。此外,匿名名称空间是 C++ 中对 C 的 static 函数的方法:

namespace {
t_ret func(const t_param& pValue) { ... }
} // << anon
注意:匿名命名空间也可能导致不必要的副本。如果您想要或需要偏离单一定义规则,并且不想在可能被“解析”的作用域中声明该符号,则可以使用它们替代静态函数。
关于 template<> 声明的最后一点。对于模板,定义必须在使用处可见,除非您的编译器支持外部模板。对于模板,您可以通过多种方式实现定义的可见性。通常,人们会直接在原地声明定义,或者添加一个包含定义的头文件,该头文件要么在头文件的末尾被包含,要么根据需要被包含。对于模板,函数不需要声明为 inline 以避免多次定义错误。

非常感谢,Justin。您能否对“可能被内联或导出”的点进行更详细的解释,或者指出我应该阅读哪些文章? - K Hein
@Justin,你忘记提到模板函数了。 - Griwes
2
@Justin 我会说模板函数是相关的,因为你没有将实现放在“.cpp”中的选项。 - juanchopanza
严格来说,您有以下选项:a)将它们的定义放在cpp文件中,前提是它们在需要时可见;b)有选择地#include定义;或c)使用extern模板。 - justin
@Justin 关于a)和b),如果尝试编译包含模板代码实现的.cpp文件,是否存在问题?或者这取决于编译器? - juanchopanza
显示剩余2条评论

24
您可以在头文件中声明函数:
namespace A {
    void foo();
}

并在 .cpp 中实现:

namespace A {
  void foo() { std::cout << "foo!"; }
}

你也可以将实现放在头文件中,确保声明为inline以避免破坏一次定义规则
namespace A {
    inline void foo() { std::cout << "foo()!"; }
}

请注意,将实现放在头文件中意味着客户端代码对实现以及用于实现的头文件都有编译依赖性。在上面的示例中,客户端代码现在依赖于“the”头文件,如果我们像在打印输出中添加感叹号这样的微不足道的更改,我们需要重新编译所有客户端代码,而不是重新链接。
非常重要的是将模板函数的实现放在头文件或由头文件包含的文件中,而不能放在.cpp文件中。
namespace B {
  template <class T>
  inline void foo(const T& t) { std::cout << t.name() << "\n"; }
}

1
在头文件中放置模板函数的实现是一个好习惯。除非你的编译器支持外部模板,否则定义必须在使用处可见。 - milesma

2

如何在头文件中声明命名空间函数?

namespace YourNamespace
{
    void fun1();
    void fun2();
}

头文件中应该只包含函数定义,就像类头文件一样,而实现应该在cpp文件中进行,还是直接在头文件中实现函数?

如果您的命名空间中的函数是静态的,那么您可以在头文件中实现函数,否则必须在cpp文件中实现。


不完全正确,您可以在头文件中实现非静态函数。 - juanchopanza
1
@juanchopanza:只有在声明为“内联”时才可以这样做。 - Kerrek SB
@juanchopanza:如果你在头文件中实现了非静态函数,并且这个头文件被多个cpp文件包含,那么你将会收到链接错误。 - Monkey Shen
@MonkeyShen 如果你声明函数为 inline,那么就不会有这个问题。请参考上面的答案。 - juanchopanza

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接