你如何在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();
如果存在名称冲突的可能性,我只会把子系统放在嵌套的命名空间中。