如何正确定义在头文件中声明的命名空间中的实体?

117
考虑一对源文件:接口声明文件(*.h或*.hpp)和其实现文件(*.cpp)。
让*.h文件如下所示:
namespace MyNamespace {
  class MyClass {
  public:
    int foo();
  };
}

我见过在源文件中使用命名空间的两种不同做法:
*.cpp 文件展示了第一种做法:
#include "MyClass.h"
using namespace MyNamespace;

int MyClass::foo() { ... }

*.cpp 显示练习 #2:

#include "MyClass.h"
namespace MyNamespace {

  int MyClass::foo() { ... }

}

我的问题是:这两种做法有什么区别,有没有一种被认为比另一种更好?

39
还有第三个选项:只使用完整的名称,例如 int MyNamespace::MyClass::foo() ... - Benjamin Bannier
1
可能是重复问题:https://dev59.com/k1zUa4cB1Zd3GeqP10kB - David
1
@Dave 不是重复问题。这些问题互为补充。建议将Dave提供的链接添加为“阅读更多...”以回答此问题。我的问题将帮助新手选择正确的风格。 - nickolay
可能是重复问题:https://dev59.com/qGsy5IYBdhLWcg3w8Slc - Firedragon
6个回答

86

就代码可读性而言,我认为使用第二种方法可能更好,原因如下:

你可以同时使用多个命名空间,在该行以下编写的任何对象或函数都可以属于这些命名空间中的任意一个(除了命名冲突)。将整个文件放在namespace块内更加明确,还可以在.cpp文件中声明属于该命名空间的新函数和变量。


戴夫在他的评论中链接的问题还概述了你正在研究的两种方法之间的差异(如果有的话)的一些关键点。 - Dan F
1
伙计们,我真的不知道该选择谁的答案。他们有交集,但又互补。 - nickolay
1
只是留言确认一下,有些 IDE (如 CLion)只有在使用选项/实践 #2 时才能检测到实现。 - pedrostanaka
@PedroTanaka 这还是现状吗?我没有注意到这样的问题。 - John McFarlane
@JMcF 我自从发表评论以来就没有再检查过。在 Clion 的早期版本中出现了这个问题。 - pedrostanaka
下面呈现的选项3 int MyNamespace::MyClass::foo() {...} 在CLion 2020.2.1中正常工作。 - pdaawr

64

最清晰的选项是你没有展示的选项:

int MyNamespace::MyClass::foo()
{
    //  ...
}

这段代码过于冗长,对大多数人来说有些难以理解。因为在我的经验中,using namespace容易导致名称冲突,所以应该避免在除非非常有限的作用域和地方使用它。因此我通常使用你提到的第二种方式。


4
非常清楚,我们一起为命名空间用户制作了一个很好的常见问题解答页面。 :) - nickolay
2
伙计们,我真的不知道该选择谁的答案。他们有交集,但又互补。 - nickolay

12

这两种做法有什么区别吗?

是的。#1和#2分别是using指令命名空间定义的示例。在这种情况下,它们实际上是相同的,但具有其他后果。例如,如果你在MyClass::foo旁引入一个新的标识符,它将拥有不同的作用域:

#1:

using namespace MyNamespace;
int x;  // defines ::x
namespace MyNamespace {
  int x;  // defines MyNamespace::x
}

哪个比另一个更好呢?

#1 优点:更加简洁;不易无意中将某些内容引入到MyNamespace中。缺点:可能会无意中引入现有的标识符。

#2 优点:更清晰地表明了现有标识符的定义和新标识符的声明都属于MyNamespace。缺点:更容易无意中将标识符引入到MyNamespace中。

对#1和#2的批评是它们都涉及整个命名空间,而你可能只关心MyNamespace:: MyClass成员的定义。这种做法太过武断并且表达意图不清楚。

一个可能的替代方案是使用using-declaration,该声明仅包含您感兴趣的标识符:

#include "MyClass.h"
using MyNamespace::MyClass;

int MyClass::foo() { ... }

7

我还想补充一点,如果由于某种原因,您决定在cpp文件中实现模板特化,并仅依赖于using namespace,则会遇到以下问题:

// .h file
namespace someNameSpace
{
  template<typename T>
    class Demo
    {
      void foo();
    };
}

// .cpp file
using namespace someNameSpace;

template<typename T>
void Demo<T>::foo(){}

// this will produce
// error: specialization of 'template<class T> void someNameSpace::Demo<T>::foo()' in different namespace [-fpermissive]
template<>
void Demo<int>::foo(){}

否则,如果您采用第二种方法,这将是可以的。

4
我想再添加一种使用声明的方法,如下所示:using-declaration
#include "MyClass.h"
using MyNamespace::MyClass;

int MyClass::foo() { ... }

这可以让你避免在一个类有很多函数时多次输入命名空间名称。

1
我认为“练习#1”根本不是正确的C++代码。这段代码片段定义了::MyClass::foo符号,而实际的完全限定名称是::MyNamespace::MyClass::foo。
要了解命名空间,您可以阅读草案的第7.3节,例如标准http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3690.pdf
这个概念相当古老,大约是1998年左右,因此您可以使用任何标准或B.Stroustroup的书籍来学习它。
在C++语言中,命名空间是一个命名范围。与类定义相反,命名空间开放添加新函数。
在C++中,“using namespace NS;”结构称为使用指令,并且可以用于我的实践中的几个目标。
  1. 你可以在另一个命名空间中使用该指令,以混合来自不同命名空间的名称。
  2. 在编译单元的上下文中,它会将同义词附加到命名空间NS中的所有变量上。

要定义符号,您可以使用两种机制 - 您可以通过在C ++源文件中在全局命名空间中操作来使用所有命名空间的显式限定。

或者您可以打开命名空间并向其中添加定义(实践#2)。


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