我有Java背景,使用的是包,而不是名称空间。我习惯于将一起工作以形成完整对象的类放入包中,然后从该包中重用它们。但是现在我在用c++工作。

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


当前回答

我所见过的大型c++项目很少使用一个以上的命名空间(例如boost库)。

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

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

我经常使用的命名空间的另一种用法(不是双关语)是匿名命名空间:

namespace {
  const int CONSTANT = 42;
}

这基本等同于:

static const int CONSTANT = 42;

然而,使用匿名命名空间(而不是静态命名空间)是在c++中仅在当前编译单元中可见的代码和数据的推荐方式。

其他回答

不要相信每个人都说名称空间只是名称空间。

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

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++名称空间时应该将其视为“接口”,并记住接口原则。

如果你想要更好地解释这种行为,你可以参考Herb Sutter的《例外c++》这本书

性病::cout

的 前缀std::表示 名称cout和endl是 在命名空间内定义 命名std。允许命名空间 我们要避免无意的碰撞 在我们定义的名称之间 以及使用这些相同的名字 在图书馆里。所有的名字 由标准库定义 都在stdnamespace中。写作std:: Cout使用作用域操作符 (运算符::)来表示我们 要使用名称cout 定义在 名称空间性病。 会显示一个更简单的方法 从库中访问名称。

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

namespace MyNamespace
{
  class MyClass
  {
  };
}

然后在代码中:

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

或者,如果你想总是使用一个特定的名称空间,你可以这样做:

using namespace MyNamespace;

MyClass* pClass = new MyClass();

编辑:按照bernhardrusch所说,我倾向于不使用“使用名称空间x”语法,我通常在实例化对象时显式指定名称空间(即我展示的第一个示例)。

正如下面所要求的,您可以使用任意数量的名称空间。

Vincent Robert在他的评论中是正确的,你如何在c++中正确使用名称空间?

使用命名空间

命名空间的使用至少有助于避免名称冲突。在Java中,这是通过“org。域名”的成语(因为它假定一个人不会使用他/她自己的域名以外的任何东西)。

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

使用“使用”

Using应该非常谨慎地使用,因为它有效地将一个(或所有)符号从名称空间导入到当前名称空间。

在头文件中这样做是邪恶的,因为你的头文件会污染包括它在内的所有源代码(这让我想起了宏…),甚至在源文件中,在函数作用域之外也是糟糕的风格,因为它会在全局作用域导入命名空间中的符号。

使用"using"最安全的方法是导入选定的符号:

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;”。原因是为了减少符号的数量,使阅读更容易,而不是因为这是一个好主意。

Scott Meyers不鼓励“使用命名空间std;”(我不记得具体是哪本书了,但如果有必要,我可以找到它)。

名称空间组成

名称空间不仅仅是包。另一个例子可以在Bjarne Stroustrup的“c++编程语言”中找到。

在“特别版”中,在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() ;
}

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

你也可以在函数中包含"using namespace…",例如:

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