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

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

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

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

namespace MyNamespace { class MyClass { }; } 

然后在代码中:

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

希望有所帮助。

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

 using namespace MyNamespace; MyClass* pClass = new MyClass(); 

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

正如你在下面所问,你可以使用任意数量的命名空间。

为了避免说一切马克·英格拉姆已经说了一点使用命名空间的小技巧:

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

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

有时命名空间用于更大的C ++项目来隐藏实现细节。

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

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

Vincent Robert在他的评论中是正确的你如何正确使用C ++的命名空间? 。

使用命名空间

命名空间至less用于避免名称冲突。 在Java中,这是通过“org.domain”成语强制执行的(因为它假定除了他/她自己的域名之外,不会使用任何东西)。

在C ++中,您可以为模块中的所有代码提供一个名称空间。 例如,对于一个模块MyModule.dll,你可以给它的代码命名空间MyModule。 我已经看到别人使用MyCompany :: MyProject :: MyModule。 我想这是过分的,但总的来说,这对我来说似乎是正确的。

使用“使用”

使用应该非常小心,因为它有效地将命名空间中的一个(或全部)符号导入到当前名称空间中。

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

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

 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 ; } 

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

“使用名称空间标准;” 斯科特·迈耶斯(Scott Meyers)不鼓励我(我不记得是哪本书,但是如果有必要,我可以find它)。

命名空间组成

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

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() ; } 

你甚至可以导入来自不同命名空间的select符号,来构build你自己的自定义命名空间接口。 我还没有find这个的实际用途,但在理论上,这是很酷的。

在其他答案中我没有看到任何提及,所以这里是我的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 ++考虑把types作为参数的函数作为该types的接口的一部分,所以不需要指定名称空间,因为参数已经隐含了名称空间。

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

 namespace ns { class A { }; } void print(A a) { } 

而且你的代码可以开始调用print(a)函数。 现在想象几年之后,作者决定提供一个print()函数,比你的函数更好,因为他知道他的class级的内部,并且可以比你的更好的版本。

然后C ++作者决定使用print()函数的版本,而不是另一个命名空间中提供的版本,以遵守接口原则。 而且这个print()函数的“升级”应该尽可能简单,这意味着你不必改变对print()函数的每一个调用。 这就是为什么可以调用“接口函数”(函数与类相同的名称空间),而无需在C ++中指定名称空间。

这就是为什么当你使用C ++命名空间作为“接口”时要考虑接口原则。

如果你想要更好地解释这种行为,你可以参考Herb Sutter的“ Exceptional C ++ ”一书

我见过的更大的C ++项目几乎不使用多个名称空间(例如boost库)。

实际上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 (也就是说,您可以在多个文件中定义一个名称空间)。

在Java中:

 package somepackage; class SomeClass {} 

在C ++中:

 namespace somenamespace { class SomeClass {} } 

使用它们,Java:

 import somepackage; 

和C ++:

 using namespace somenamespace; 

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

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

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

@ 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 ::现在每个都有一个shared_ptr。]

一般来说,如果我认为可能存在与其他库函数或types名称冲突的情况,我会为代码体创build一个名称空间。 这也有助于品牌代码,阿拉提升:: 。

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

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

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

我在C#,Perl等方面使用C ++命名空间。它只是标准库的东西,第三方的东西和我自己的代码之间的符号的语义分离。 我会把我自己的应用程序放在一个命名空间中,然后将另一个命名空间中的可重用的库组件分开。

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

 #include "lib/module1.h" #include "lib/module2.h" lib::class1 *v = new lib::class1(); 

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