在 C++ 中,命名空间是一个相关的名称或标识符(函数、类、变量)的集合,它有助于将这些标识符与来自其他命名空间或全局命名空间的相似标识符区分开来。
让我们看下面的代码
int main() {
int var;
// Error: conflicting declaration
double var;
}
这段代码无法成功编译,因为我们在 main()
函数的作用域内声明了两个同名的变量 var。在 C++ 中,这被称为**命名冲突**。
一种无需错误地编译代码的方法是,将其中一个 var 声明在另一个独立于其他 var 的作用域中。
int main() {
int var;
// separate local scope
{
double var;
}
return 0;
}
但是,如果我们使用命名空间,就可以在同一作用域内为多个变量(或数组、向量、函数等)使用相同的名称。
换句话说,C++ 中的命名空间是一种防止程序中命名冲突的方法,尤其是在大型项目中。
创建命名空间
我们可以使用 namespace
关键字创建命名空间,并在其作用域内声明/定义我们的实体。
namespace dbl {
double var;
}
在这里,我们创建了一个名为 dbl
的命名空间,并在其中声明了一个名为 var 的 double
变量。
然后,我们可以使用命名空间外的作用域解析运算符 ::
来指定我们使用的是属于 dbl
的 var 变量。
dbl::var;
让我们用一个简单的例子来阐明这一点。
#include <iostream>
using namespace std;
// create a namespace with a double variable
namespace dbl {
double var;
}
int main() {
// create an int variable of the same name
int var = 5;
// use the created namespace to avoid naming conflict
dbl::var = 5.55;
// display the variables
cout << "dbl var = " << dbl::var << endl;
cout << "main var = " << var << endl;
return 0;
}
输出
dbl var = 5.55 main var = 5
在这里,我们创建了一个 dbl
命名空间,并在其中定义了一个 double
变量 var。
然后,我们在 main()
中定义了另一个 int
类型的 var。通过使用代码 dbl::var
,我们可以使用 dbl
命名空间中的变量以及 main()
中的变量。
因此,我们可以看到,使用 dbl
命名空间有效地防止了程序中出现命名冲突。
多个和嵌套的命名空间
我们可以在单个程序中使用多个命名空间。当编写包含大量代码的大型程序时,多个命名空间尤其有用。
同样,命名空间可以嵌套在其他命名空间中,这使我们能够以逻辑和分层的方式组织代码。
下面提供了一个多重和嵌套命名空间的示例
#include <iostream>
using namespace std;
namespace one {
void display() {
cout << "namespace one" << endl;
}
// create another namespace inside namespace one
namespace one_one {
void display() {
cout << "namespace one_one" << endl;
}
}
}
namespace two {
void display() {
cout << "namespace two" << endl;
}
}
void display() {
cout << "not a namespace" << endl;
}
int main() {
// call the display() function of namespace one
one::display();
// call the display() function of namespace one_one
one::one_one::display();
// call the display() function of namespace two
two::display();
// call the display() function outside the namespaces
display();
return 0;
}
输出
namespace one namespace one_one namespace two not a namespace
在这里,我们创建了 one
和 two
两个命名空间,每个命名空间都有自己的 display()
函数。
我们还在嵌套在 one
命名空间中的 one_one
命名空间中定义了另一个 display()
函数。
然后,我们在所有命名空间之外声明了一个 display()
函数。
然后,我们使用 ::
运算符调用了每个命名空间中的 display()
函数。
注意我们调用嵌套命名空间 one_one
中 display()
函数的方式。
one::one_one::display();
正如你所看到的,我们需要同时包含外部命名空间 one
和嵌套命名空间 one_one
才能访问嵌套命名空间的成员。
最后,如果不使用 ::
运算符,则会调用未在命名空间内定义的函数。
// call the display() function outside the namespaces
display();
C++ using 指令
借助 using
指令,我们可以绕过 ::
运算符的使用。
事实上,到目前为止,我们已经在大部分程序中使用了 using namespace std;
这段代码。
我们已经提到,如果不使用 std
命名空间,我们一些最重要的 IO 对象和标准异常将
// without using std namespace
std::cout
std::cin
std::endl
std::exception
std::bad_cast
通过在程序中包含 using namespace std;
代码,我们可以省略 std
命名空间中定义的标识符的 std::
部分。
// using std namespace
cout
cin
endl
exception
bad_cast
这同样适用于其他命名空间。
示例:C++ using 指令
我们可以在单个程序中创建和使用多个命名空间。
#include <iostream>
using namespace std;
namespace one {
void display() {
cout << "namespace one" << endl;
}
}
namespace two {
void display() {
cout << "namespace two" << endl;
}
}
int main() {
using namespace one;
// call one::display()
display();
// call two:: display
two::display();
return 0;
}
输出
namespace one namespace two
在这里,通过在程序中包含 using namespace one;
代码,我们可以调用其 display()
函数,而无需使用作用域解析运算符 ::
。
如果我们想调用 two
命名空间中的 display()
函数,则需要使用 ::
运算符。
注意: 我们已将 using namespace one;
指令包含在 main()
中,而 using namespace std;
代码则位于 main()
(以及所有其他可能的范围)之外。
#include <iostream>
using namespace std;
... .. ...
... .. ...
int main() {
using namespace one;
... .. ...
}
这意味着我们在 main()
函数的作用域内使用了 one
命名空间,而 std
命名空间则在全局作用域中使用。
命名空间的使用可以是全局的,也可以限制在特定范围。这取决于我们如何使用命名空间。
using 指令的命名冲突
从我们刚才看到的示例程序可以清楚地看出,using
指令会在我们的程序中产生许多命名冲突。例如,
using namespace one;
namespace one {
void display() {...}
}
void display() {...}
int main() {
// Error: more than one instance of display()
display();
return 0;
}
在上面的代码中,我们在全局作用域中使用了 one
命名空间。
因此,当我们调用 display()
函数时,编译器无法确定我们是调用了属于 one
命名空间中的函数,还是调用了命名空间外的函数。
当我们在同一标识符中使用多个命名空间时,也可能发生命名冲突。例如,
namespace one {
void display() {...}
}
namespace two {
void display() {...}
}
using namespace one;
using namespace two;
int main() {
// Error: more than one instance of display()
display();
return 0;
}
std 命名空间
C++ 标准库中的所有文件都在 std
命名空间中声明了所有实体。
但是,C++ 标准中的许多实体(函数、对象等)可能与其他库中的命名空间共享标识符。
或者它们可能与我们在程序中手动创建的标识符共享。
这就是为什么将 using namespace std;
代码包含在全局作用域中被认为是一种不好的做法。
例如,
#include <iostream>
using namespace std;
string cout() {
return "\n";
}
int main() {
// Error: cout is ambiguous
cout << "Testing!" << endl;
return 0;
}
在这里,我们定义了自己的 cout()
函数,同时也使用了 std
命名空间。这会导致程序中的命名冲突,并且在使用 cout
时会出错。