如何在C ++中正确使用名称空间?

时间:2008-09-03 12:54:07

标签: c++ namespaces

我来自Java背景,使用包,而不是命名空间。我习惯于将一起工作的类放在一起形成一个完整的对象,然后再从该包中重用它们。但现在我正在使用C ++。

如何在C ++中使用命名空间?您是为整个应用程序创建单个名称空间,还是为主要组件创建名称空间?如果是这样,如何从其他命名空间中的类创建对象?

16 个答案:

答案 0 :(得分:161)

命名空间本质上是包。它们可以像这样使用:

namespace MyNamespace
{
  class MyClass
  {
  };
}

然后在代码中:

MyNamespace::MyClass* pClass = new MyNamespace::MyClass();

希望有所帮助。

或者,如果您想要始终使用特定的命名空间,则可以执行以下操作:

using namespace MyNamespace;

MyClass* pClass = new MyClass();

编辑:按照bernhardrusch的说法,我根本不会使用“using namespace x”语法,我通常在实例化对象时明确指定命名空间(即我展示的第一个例子。)

当你问below时,你可以使用任意数量的命名空间。

答案 1 :(得分:112)

为了避免说Mark Ingram已经说了一些使用命名空间的小提示:

避免头文件中的“using namespace”指令 - 这将打开导入此头文件的程序的所有部分的命名空间。在实现文件(* .cpp)中,这通常不是什么大问题 - 尽管我更喜欢在函数级别使用“using namespace”指令。

我认为命名空间主要用于避免命名冲突 - 不一定要组织代码结构。我主要用头文件/文件结构来组织C ++程序。

有时在较大的C ++项目中使用名称空间来隐藏实现细节。

使用指令的附加说明: 有些人更喜欢仅使用“使用”单个元素:

using std::cout;  
using std::endl;

答案 2 :(得分:76)

文森特罗伯特在他的评论How do you properly use namespaces in C++?中是正确的。

使用命名空间

命名空间至少用于帮助避免名称冲突。在Java中,这是通过“org.domain”习惯来强制执行的(因为假设人们不会使用除他/她自己的域名之外的其他内容)。

在C ++中,您可以为模块中的所有代码指定命名空间。例如,对于模块MyModule.dll,您可以将其代码命名为MyModule。我在其他地方看到有人使用MyCompany :: MyProject :: MyModule。我想这太过分了,但总而言之,这对我来说似乎是正确的。

使用“使用”

应谨慎使用,因为它可以有效地将命名空间中的一个(或所有)符号导入当前命名空间。

在头文件中这样做是邪恶的,因为你的标题会污染包括它的每个源(它让我想起宏......),甚至在源文件中,在函数范围之外的坏样式,因为它会导入在全局范围内,来自命名空间的符号。

使用“使用”最安全的方法是导入选择符号:

void doSomething()
{
   using std::string ; // string is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   std::cout << a << std::endl ;
}

void doSomethingElse()
{
   using namespace std ; // everything from std is now "imported", at least,
                       // until the end of the function
   string a("Hello World!") ;
   cout << a << endl ;
}

你会看到很多“使用命名空间std;”在教程或示例代码中。原因是减少了符号的数量,使阅读更容易,而不是因为这是一个好主意。

“using namespace std;”斯科特迈尔斯气馁(我不记得究竟是哪本书,但如果有必要,我可以找到它。)

命名空间组合

命名空间不仅仅是包。另一个例子可以在Bjarne Stroustrup的“The C ++ Programming Language”中找到。

在“特别版”的 8.2.8命名空间组合中,他描述了如何将两个名称空间AAA和BBB合并到另一个名为CCC的名称空间中。因此,CCC成为AAA和BBB的别名:

namespace AAA
{
   void doSomething() ;
}

namespace BBB
{
   void doSomethingElse() ;
}

namespace CCC
{
   using namespace AAA ;
   using namespace BBB ;
}

void doSomethingAgain()
{
   CCC::doSomething() ;
   CCC::doSomethingElse() ;
}

您甚至可以从不同的命名空间导入选择符号,以构建您自己的自定义命名空间界面。我还没有找到实际用途,但从理论上讲,它很酷。

答案 3 :(得分:71)

我在其他答案中没有看到任何提及,所以这是我的2加分:

在“using namespace”主题上,一个有用的语句是命名空间别名,允许您“重命名”命名空间,通常是为了给它一个较短的名称。例如,而不是:

Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::TheClassName foo;
Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally::AnotherClassName bar;

你可以写:

namespace Shorter = Some::Impossibly::Annoyingly::Long:Name::For::Namespace::Finally;
Shorter::TheClassName foo;
Shorter::AnotherClassName bar;

答案 4 :(得分:53)

不要听每个人告诉你名称空间只是名称空间。

它们很重要,因为编译器认为它们应用了接口原理。基本上,它可以用一个例子来解释:

namespace ns {

class A
{
};

void print(A a)
{
}

}

如果你想打印A对象,代码就是这个:

ns::A a;
print(a);

请注意,我们在调用函数时没有明确提到命名空间。这是接口原则:C ++认为函数将类型作为参数作为该类型接口的一部分,因此不需要指定命名空间,因为该参数已经隐含了命名空间。

为什么这个原则很重要?想象一下,A类作者没有为这个类提供print()函数。你必须自己提供一个。由于您是一名优秀的程序员,您将在自己的命名空间中定义此函数,或者在全局命名空间中定义。

namespace ns {

class A
{
};

}

void print(A a)
{
}

您的代码可以随时随地调用print(a)函数。现在想象多年以后,作者决定提供一个print()函数,比你的更好,因为他知道他的班级内部,并且可以制作比你更好的版本。

然后C ++作者决定使用他的print()函数版本而不是另一个名称空间中提供的版本来尊重接口原则。而且print()函数的这种“升级”应该尽可能简单,这意味着您不必更改每次调用print()函数。这就是为什么可以在不用C ++指定命名空间的情况下调用“接口函数”(与类在同一名称空间中的函数)的原因。

这就是为什么在使用C ++命名空间时应该将其视为“接口”并牢记接口原则。

如果您想要更好地解释此行为,可以参考书籍Exceptional C++ from Herb Sutter

答案 5 :(得分:36)

  

我见过的较大的C ++项目几乎没有使用多个命名空间(例如boost库)。

实际上,boost使用了大量的命名空间,通常boost的每个部分都有自己的内部工作空间,然后可能只将公共接口放在顶级命名空间中。

我个人认为,即使在单个应用程序(或库)中,代码库越大,命名空间就越重要。在工作中,我们将应用程序的每个模块放在自己的命名空间中。

我经常使用的命名空间的另一个用途(没有双关语)是匿名命名空间:

namespace {
  const int CONSTANT = 42;
}

这基本上与:

相同
static const int CONSTANT = 42;

使用匿名命名空间(而不是静态)是建议的方法,使代码和数据仅在C ++中的当前编译单元中可见。

答案 6 :(得分:18)

另请注意,您可以添加到命名空间。这个例子更清楚,我的意思是你可以拥有:

namespace MyNamespace
{
    double square(double x) { return x * x; }
}

在文件square.h

namespace MyNamespace
{
    double cube(double x) { return x * x * x; }
}

在文件cube.h中。这定义了一个名称空间MyNamespace(也就是说,您可以在多个文件中定义一个名称空间)。

答案 7 :(得分:11)

在Java中:

package somepackage;
class SomeClass {}

在C ++中:

namespace somenamespace {
    class SomeClass {}
}

使用它们,Java:

import somepackage;

和C ++:

using namespace somenamespace;

此外,全名是Java的“somepackge.SomeClass”和C ++的“somenamespace :: SomeClass”。使用这些约定,您可以像在Java中习惯一样进行组织,包括为命名空间创建匹配的文件夹名称。但是文件夹 - &gt;包和文件 - &gt;类要求不存在,因此您可以在包和命名空间之外单独命名文件夹和类。

答案 8 :(得分:5)

@ marius

是的,您可以一次使用多个名称空间,例如:

using namespace boost;   
using namespace std;  

shared_ptr<int> p(new int(1));   // shared_ptr belongs to boost   
cout << "cout belongs to std::" << endl;   // cout and endl are in std

[二月2014年 - (它真的那么长吗?):这个特殊的例子现在含糊不清,正如乔伊在下面指出的那样。 Boost和std :: now每个都有一个shared_ptr。]

答案 9 :(得分:5)

您还可以在函数中包含“using namespace ...”,例如:

void test(const std::string& s) {
    using namespace std;
    cout << s;
}

答案 10 :(得分:3)

一般来说,如果我认为可能存在与其他库冲突的函数或类型名称,我会为代码体创建一个命名空间。它还有助于品牌代码,ala boost::

答案 11 :(得分:3)

我更喜欢为应用程序使用顶级命名空间,为组件使用子命名空间。

您可以使用其他命名空间中的类的方式与java中的方式非常相似。 您可以使用“使用NAMESPACE”,它类似于“import PACKAGE”语句,例如:使用标准或者您将包指定为以“::”分隔的类的前缀,例如的std :: string。这类似于Java中的“java.lang.String”。

答案 12 :(得分:3)

请注意,C ++中的命名空间实际上只是一个名称空间。它们不提供Java中的任何封装,因此您可能不会使用它们。

答案 13 :(得分:2)

我使用C ++命名空间的方式与我在C#,Perl等中的方式相同。它只是标准库内容,第三方内容和我自己的代码之间符号的语义分离。我将自己的应用程序放在一个命名空间中,然后将另一个命名空间中的可重用库组件放在一起进行分离。

答案 14 :(得分:2)

java和C ++之间的另一个区别是,在C ++中,命名空间层次结构不需要加工文件系统布局。所以我倾向于将一个完整的可重用库放在一个命名空间中,并将子系统放在子目录库中:

#include "lib/module1.h"
#include "lib/module2.h"

lib::class1 *v = new lib::class1();

如果存在名称冲突的可能性,我只会将子系统放在嵌套的命名空间中。

答案 15 :(得分:-1)

标准:: cout

该 前缀 std:: 表示 名称 cout 和 endl 是 在命名空间内定义 命名标准。命名空间允许 我们避免无意的碰撞 在我们定义的名字之间 和使用那些相同的名字 图书馆内。所有的名字 由标准库定义 在 stdnamespace 中。写标准:: cout 使用范围运算符 (::operator) 说我们 想用 cout 这个名字 中定义的 命名空间标准。 将展示一个更简单的方法 从库中访问名称。