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

How do you properly use namespaces in C++?

提问人:Marius 提问时间:9/3/2008 最后编辑:Philipp Matthias SchäferMarius 更新时间:3/22/2021 访问量:208556

问:

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

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

C++ 命名空间

评论


答:

177赞 Mark Ingram 9/3/2008 #1

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

namespace MyNamespace
{
  class MyClass
  {
  };
}

然后在代码中:

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

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

using namespace MyNamespace;

MyClass* pClass = new MyClass();

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

正如您在下面所问的,您可以根据需要使用任意数量的命名空间。

评论

29赞 Tom Savage 12/5/2009
IMO,最好习惯于将命名空间作为符号的前缀,而不是根本不使用。所以我总是写或现在,因为这就是我现在所说的。我永远不会只是写.stdusingstd::coutstd::stringcout
5赞 jkerian 10/30/2010
虽然这是非常正确的,但我个人发现,当你处理较小的库时,这要不那么重要。通常,您可以只使用 ,特别是当您使用库中的大量类型时。stdusing namespace FooBario;
4赞 Alan Turing 5/28/2011
@jkerian,我明白你的意思,但我不同意,因为名称冲突(在我看来)更有可能来自这样的小库。大多数人都小心翼翼地避免将类/函数命名为与 STL 中的类/函数相同。也就是说,我同意如果可能的话,应该避免在头文件中这样做。using namespace X;
15赞 10/24/2011
@LexFridman “大多数人都小心翼翼地不要用 STL 中的类/函数来命名类/函数”——事实并非如此。例如,如果我要为一些奇怪的硬件编写一些非常专业的 I/O 代码,我永远不会使用其他任何东西来表示我自己的特殊换行符序列。我的意思是,为什么要发明名字?mylibrary::endl
0赞 bgenchel 11/13/2015
我的编译器仍然无法识别命名空间,即使我想显式指定它并包含声明它的文件。
3赞 Adam Hollidge 9/3/2008 #2

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

3赞 dmeister 9/3/2008 #3

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

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

6赞 Adam Hollidge 9/3/2008 #4

@马吕斯

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

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 年 2 月 -- (真的有那么久吗?):这个特殊的例子现在是模棱两可的,正如 Joey 在下面指出的那样。Boost 和 std::现在各有一个shared_ptr。

评论

2赞 Joey 2/7/2014
请注意,现在也有,因此当您尝试使用 .stdshared_ptrbooststdshared_ptr
2赞 paddy 3/16/2016
这是一个很好的例子,说明为什么许多软件公司不鼓励以这种方式导入整个命名空间。始终指定命名空间并没有什么坏处,如果它们太长,则从命名空间中创建一个别名或仅创建重要的特定类。
12赞 Staale 9/3/2008 #5

在 Java 中:

package somepackage;
class SomeClass {}

在 C++ 中:

namespace somenamespace {
    class SomeClass {}
}

使用它们,Java:

import somepackage;

和 C++:

using namespace somenamespace;

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

123赞 bernhardrusch 9/3/2008 #6

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

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

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

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

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

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

评论

2赞 idij 12/19/2014
按照您的建议,在函数级别而不是在 .cpp 文件级别或 .cpp 块级别“使用命名空间”的一个优点是,它对单编译单元构建有很大帮助。“using namespace”是可传递的,适用于同一单元中离散命名空间 A {} 块的命名空间 A,因此对于单个编译单元构建,如果它们在文件或命名空间块级别完成,您很快就会使用所有内容。
0赞 Konstantin 4/19/2015
using std::cout;是一个 using 声明
3赞 AlQuemist 4/29/2016
是否可以在单个语句中使用单个命名空间中的多个名称?类似甚至,.using std::cout, std::endl;using std::cout, endl;
0赞 Praxeolitic 6/10/2016
如果标头位于另一个命名空间中,则可以在标头中使用 a。一般来说,我不推荐它,但它不会污染全局命名空间。using namespace x
20赞 OysterD 9/3/2008 #7

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

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

在文件中,并且square.h

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

在文件中。这将定义单个命名空间(即,您可以跨多个文件定义单个命名空间)。cube.hMyNamespace

4赞 Kristopher Johnson 9/3/2008 #8

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

2赞 spoulson 9/3/2008 #9

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

2赞 KeithB 9/3/2008 #10

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

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

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

只有当存在名称冲突的可能性时,我才会将子系统放在嵌套的命名空间中。

5赞 Shadow2531 9/3/2008 #11

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

void test(const std::string& s) {
    using namespace std;
    cout << s;
}
37赞 Andreas Magnusson 9/7/2008 #12

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

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

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

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

namespace {
  const int CONSTANT = 42;
}

这与以下基本相同:

static const int CONSTANT = 42;

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

评论

14赞 sellibitze 10/21/2009
您的两个示例都等效于,因为命名空间范围内的顶级 const 已经暗示了内部链接。因此,在这种情况下,您不需要匿名命名空间。const int CONSTANT = 42;
60赞 Vincent Robert 9/7/2008 #13

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

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

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

评论

25赞 Eclipse 10/26/2008
如果添加了 ns::P rint,您实际上必须更改对 print() 的每个调用,但编译器会将每个调用标记为不明确。默默地切换到新功能将是一个糟糕的主意。
1赞 Vaska el gato 6/22/2016
我现在想知道,@Vincent说过,如果 autor 提供 ns::P rint() 函数,您将不得不将所有调用更改为 print,您想说什么?当作者添加 ns::P rint() 函数时,您可以删除自己的实现吗?或者你只是使用 ns::p rint() using-declaration 添加?还是其他的?谢谢
85赞 paercebal 9/17/2008 #14

文森特·罗伯特(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 ;
}

您将在教程或示例代码中看到很多“using namespace std ;”。原因是减少符号的数量以使阅读更容易,而不是因为这是一个好主意。

斯科特·迈耶斯(Scott Meyers)不鼓励使用“使用命名空间std”(我不记得确切是哪本书,但如果有必要,我可以找到它)。

命名空间组成

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

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

评论

0赞 yanpas 1/10/2016
您能否澄清一下,请“为模块中的所有代码提供一个命名空间”?封装到模块的好做法是什么?例如,我有一类复数和与复数相关的外部函数。这个类和这两个函数应该在一个命名空间中吗?
0赞 D.L 7/1/2023
你会在教程或示例代码中看到很多“using namespace std ;”的内容。解释者从未提到这是糟糕的做法,但它恰恰是针对那些试图学习的人。
84赞 Éric Malenfant 10/21/2009 #15

我在其他答案中没有看到任何提及它,所以这是我的 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;
-1赞 nidhi kumari 3/22/2021 #16

标准 :: cout

这 prefix std:: 表示 名称 Cout 和 endl 是 在命名空间中定义 命名为 std。命名空间允许 我们避免无意中的碰撞 在我们定义的名称之间 以及这些相同名称的使用 在图书馆内。所有名称 由标准库定义 位于 stdnamespace 中。写作 std:: cout 使用 scope 运算符 (::运算符)说我们 想要使用名称 Cout 在 命名空间 std。 将显示一种更简单的方法 访问库中的名称。