我们如何在C ++中正确使用名称空间?
我来自Java背景,这里使用的是包,而不是名称空间。我习惯于将可以一起工作的类组合成一个完整的对象放入包中,然后再从该包中重用它们。但是现在我正在使用C ++。
我们如何在C ++中使用名称空间?我们是为整个应用程序创建单个名称空间,还是为主要组件创建名称空间?如果是这样,如何从其他命名空间中的类创建对象?
解决方案
回答
命名空间本质上是软件包。它们可以像这样使用:
namespace MyNamespace { class MyClass { }; }
然后在代码中:
MyNamespace::MyClass* pClass = new MyNamespace::MyClass();
希望能有所帮助。
或者,如果我们希望始终使用特定的名称空间,则可以执行以下操作:
using namespace MyNamespace; MyClass* pClass = new MyClass();
编辑:按照bernhardrusch所说,我倾向于完全不使用" using namespace x"语法,我通常在实例化对象时明确指定名称空间(即,我显示的第一个示例)。
并按照下面的要求,我们可以使用任意数量的名称空间。
回答
一般而言,如果我认为可能与其他库存在函数或者类型名称冲突,则为代码主体创建一个名称空间。它还有助于商标代码,也可以提高::。
回答
我更喜欢为应用程序使用顶级名称空间,为组件使用子名称空间。
令人惊讶的是,我们可以使用其他命名空间中的类的方式与java中的方式非常相似。
我们可以使用类似于" import PACKAGE"语句的" use NAMESPACE",例如使用标准或者,我们将软件包指定为类的前缀,并以" ::"分隔,例如std :: string。这类似于Java中的" java.lang.String"。
回答
@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年-(真的有那么长时间吗?):这个特定示例现在模棱两可,如Joey在下面指出。 Boost和std :::现在每个都有一个shared_ptr。]
回答
在Java中:
package somepackage; class SomeClass {}
在C ++中:
namespace somenamespace { class SomeClass {} }
并使用它们,Java:
import somepackage;
和C ++:
using namespace somenamespace;
同样,全名是Java的" somepackge.SomeClass"和C ++的" somenamespace :: SomeClass"。使用这些约定,我们可以像在Java中一样习惯进行组织,包括为名称空间创建匹配的文件夹名称。文件夹->包和文件->类要求不存在,因此我们可以根据包和名称空间独立命名文件夹和类。
回答
为了避免说出一切,Mark Ingram已经说了一些使用命名空间的提示:
避免在头文件中使用"使用名称空间"指令,这将为导入该头文件的程序的所有部分打开名称空间。在实现文件(* .cpp)中,这通常不是什么大问题,尽管我更喜欢在功能级别使用" using namespace"指令。
我认为名称空间主要用于避免命名冲突,而不必组织代码结构。我主要用头文件/文件结构来组织C ++程序。
有时,较大的C ++项目中使用名称空间来隐藏实现细节。
有关using指令的添加说明:
有些人更喜欢仅对单个元素使用"使用":
using std::cout; using std::endl;
回答
另外,请注意,我们可以添加到名称空间。用一个例子更清楚,我的意思是你可以拥有:
namespace MyNamespace { double square(double x) { return x * x; } }
在文件square.h中,以及
namespace MyNamespace { double cube(double x) { return x * x * x; } }
在文件cube.h中。这定义了一个名称空间" MyNamespace"(即,我们可以在多个文件中定义一个名称空间)。
回答
请注意,C ++中的名称空间实际上只是一个名称空间。它们没有提供程序包在Java中所做的任何封装,因此我们可能不会过多地使用它们。
回答
我使用C ++名称空间的方式与在C#,Perl等系统中使用的方式相同。这只是标准库内容,第三方内容和我自己的代码之间符号的语义分离。我将自己的应用放在一个名称空间中,然后将一个可重用的库组件放在另一个名称空间中以进行分离。
回答
Java和C ++之间的另一个区别是,在C ++中,名称空间层次结构不需要处理文件系统布局。因此,我倾向于将整个可重用的库放在单个名称空间中,并将库中的子系统放在子目录中:
#include "lib/module1.h" #include "lib/module2.h" lib::class1 *v = new lib::class1();
如果可能发生名称冲突,我只会将子系统放在嵌套的名称空间中。
回答
我们还可以在函数内包含" using namespace ...",例如:
void test(const std::string& s) { using namespace std; cout << s; }
回答
Bigger C++ projects I've seen hardly used more than one namespace (e.g. boost library).
实际上,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的《 Exceptional C ++》一书。
回答
文森特·罗伯特(Vincent Robert)的评论正确无误。如何在C ++中正确使用名称空间?
使用名称空间
至少使用命名空间来避免名称冲突。在Java中,这是通过" org.domain"惯用语强制执行的(因为它假定除了他/她自己的域名外,其他任何人都不会使用)。
在C ++中,我们可以为模块中的所有代码提供名称空间。例如,对于模块MyModule.dll,可以为其代码命名空间MyModule。我在其他地方看到有人在使用MyCompany :: MyProject :: MyModule。我想这是矫kill过正,但总的来说,对我来说似乎是正确的。
使用"使用"
使用时应格外小心,因为它可以将一个(或者所有)符号从一个名称空间有效地导入我们当前的名称空间。
在标头文件中执行此操作是邪恶的,因为标头会污染包括它的每个源(它使我想起宏...),甚至在源文件中,函数作用域之外的样式也很糟糕,因为它将在全局范围内导入命名空间中的符号。
使用"使用"的最安全方法是导入选择符号:
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;