当前位置:  开发笔记 > 编程语言 > 正文

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

如何解决《如何在C++中正确使用名称空间?》经验,为你挑选了10个好方法。

我来自Java背景,使用包,而不是命名空间.我习惯于将一起工作的类放在一起形成一个完整的对象,然后再从该包中重用它们.但现在我在使用C++.

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



1> Mark Ingram..:

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

namespace MyNamespace
{
  class MyClass
  {
  };
}

然后在代码中:

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

希望有所帮助.

或者,如果要始终使用特定命名空间,则可以执行以下操作:

using namespace MyNamespace;

MyClass* pClass = new MyClass();

编辑:遵循bernhardrusch所说的,我倾向于根本不使用"using namespace x"语法,我通常在实例化我的对象时明确指定命名空间(即我展示的第一个例子).

正如您在下面提到的那样,您可以使用任意数量的命名空间.


IMO最好习惯于将`std`命名空间添加到符号前面而不是使用`using`.所以我现在总是写`std :: cout`或`std :: string`,因为这就是我现在所说的.我永远不会写'cout`.
@LexFridman"大多数人都小心不要将类/函数命名为与STL中的相同" - 这是不正确的.例如,如果我为一些奇怪的硬件编写一些非常专业的I/O代码,我永远不会使用除了`mylibrary :: endl`以外的任何东西来表示我自己的特殊换行序列.我的意思是,为什么发明名字?
虽然对于`std`来说这是非常正确的,但是当你处理较小的库时,我个人发现这一点并不那么重要.通常你可以使用`using namespace FooBario;`,特别是如果你在库中使用了相当多的类型.
@jkerian,我明白你的观点,但我不同意,因为名字碰撞(在我看来)更可能来自这些小型图书馆.大多数人都小心不要将类/函数命名为与STL中的类相同.也就是说,我同意如果可能的话,应该在头文件中避免使用命名空间X;`.

2> bernhardrusc..:

为了避免说Mark Ingram已经说了一些使用命名空间的小提示:

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

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

有时在较大的C++项目中使用名称空间来隐藏实现细节.

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

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


是否可以在单个语句中使用_single_名称空间中的_several_名称?像`使用std :: cout,std :: endl;`甚至,`使用std :: cout,endl;`.

3> paercebal..:

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

使用命名空间

命名空间至少用于帮助避免名称冲突.在Java中,这是通过"org.domain"惯用法强制实施的(因为假设人们不会使用除他/她自己的域名之外的其他内容).

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

使用"使用"

应谨慎使用使用,因为它有效地将命名空间中的一个(或所有)符号导入到当前命名空间中.

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

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

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

"using namespace std;" 斯科特迈尔斯气馁(我不记得究竟是哪本书,但如果有必要,我可以找到它).

命名空间组合

命名空间不仅仅是包.另一个例子可以在Bjarne Stroustrup的"The C++ Programming Language"中找到.

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

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



4> Éric Malenfa..:

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

在"using namespace"主题中,一个有用的语句是命名空间别名,允许您"重命名"命名空间,通常是为了给它一个更短的名称.例如,而不是:

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;



5> Vincent Robe..:

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

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

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


如果添加ns :: Print,您实际上必须更改每次调用print(),但编译器会将每个调用标记为不明确.默默地切换到新功能将是一个糟糕的主意.

6> 小智..:

我见过的较大的C++项目几乎没有使用多个命名空间(例如boost库).

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

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

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

namespace {
  const int CONSTANT = 42;
}

这基本上与以下相同:

static const int CONSTANT = 42;

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


这两个示例都等同于`const int CONSTANT = 42;`因为命名空间范围中的顶级const已经暗示了内部链接.所以在这种情况下你不需要匿名命名空间.

7> OysterD..:

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

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

在一个文件中square.h,和

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

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



8> Staale..:

在Java中:

package somepackage;
class SomeClass {}

在C++中:

namespace somenamespace {
    class SomeClass {}
}

使用它们,Java:

import somepackage;

和C++:

using namespace somenamespace;

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



9> Adam Hollidg..:

@ marius

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

using namespace boost;   
using namespace std;  

shared_ptr p(new int(1));   // shared_ptr belongs to boost   
cout << "cout belongs to std::" << endl;   // cout and endl are in std

[二月 2014年 - (它真的那么长吗?):这个特殊的例子现在含糊不清,正如乔伊在下面指出的那样.Boost和std :: now每个都有一个shared_ptr.]


请注意,`std`现在也有`shared_ptr`,因此当你尝试使用`shared_ptr`时,同时使用`boost`和`std`命名空间会发生冲突.
这是一个很好的例子,说明为什么许多软件公司会以这种方式阻止导入整个命名空间.总是指定命名空间并没有什么坏处,如果它们太长,那么从命名空间中创建别名或只有重要的特定类.

10> Shadow2531..:

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

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

推荐阅读
夏晶阳--艺术
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有