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

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

有帮助吗?

解决方案

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

namespace MyNamespace
{
  class MyClass
  {
  };
}

然后在代码中:

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

希望有帮助。

或者,如果您想始终使用特定的命名空间,您可以这样做:

using namespace MyNamespace;

MyClass* pClass = new MyClass();

编辑: 遵循什么 伯恩哈德鲁施 已经说过,我倾向于根本不使用“using namespace x”语法,我通常在实例化对象时显式指定名称空间(即我展示的第一个例子)。

正如你所问的 以下, ,您可以使用任意多个名称空间。

其他提示

为了避免说出所有内容,Mark Ingram 已经说了一些使用命名空间的小技巧:

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

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

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

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

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

文森特·罗伯特的评论是正确的 如何在 C++ 中正确使用命名空间?.

使用命名空间

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

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

使用“使用”

使用时应非常小心,因为它可以有效地将一个(或所有)符号从命名空间导入到当前命名空间中。

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

使用“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”;在教程或示例代码中。原因是减少符号的数量以使阅读更容易,而不是因为这是一个好主意。

“使用名称空间std;”斯科特·迈耶斯(Scott Meyers)劝阻(我不记得确切的书籍,但是如有必要,我可以找到它)。

命名空间组成

命名空间不仅仅是包。另一个例子可以在 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() ;
}

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

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

在“使用命名空间”主题上,一个有用的语句是命名空间别名,它允许您“重命名”命名空间,通常是给它一个更短的名称。例如,代替:

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;

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

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

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++

我见过的较大的 C++ 项目几乎不会使用多个命名空间(例如增强库)。

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

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

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

namespace {
  const int CONSTANT = 42;
}

这基本上与:

static const int CONSTANT = 42;

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

另请注意,您可以添加到命名空间。通过一个例子就更清楚了,我的意思是你可以:

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

在一个文件中 square.h, , 和

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

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

在爪哇中:

package somepackage;
class SomeClass {}

在 C++ 中:

namespace somenamespace {
    class SomeClass {}
}

并使用它们,Java:

import somepackage;

和 C++:

using namespace somenamespace;

此外,Java 的全名是“somepackge.SomeClass”,C++ 的全名是“somenamespace::SomeClass”。使用这些约定,您可以像在 Java 中那样进行组织,包括为命名空间创建匹配的文件夹名称。不过,文件夹->包和文件->类要求不存在,因此您可以独立于包和命名空间来命名文件夹和类。

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

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

@马吕斯

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

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年——(真的有那么久吗?):正如乔伊在下面指出的那样,这个特定的例子现在是模棱两可的。升压和标准::现在每个都有一个shared_ptr。]

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

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

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

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

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

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

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

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

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top