举一个小例子:
假设您是第一次开车去朋友家,给您的地址是 Mill City 的 245 Front Street。到达米尔城后,您拿出地图,却发现米尔城实际上有两条不同的前街,彼此隔着城镇!你会去哪一个?除非有其他线索可以帮助您做出决定(例如,您记得他的房子在河边),否则您必须打电话给您的朋友并询问更多信息。因为这会令人困惑且效率低下(尤其是对于您的邮递员),所以在大多数国家/地区,城市内的所有街道名称和房屋地址都必须是唯一的。
类似地,C++ 要求所有标识符都没有歧义。如果以编译器或链接器无法区分的方式将两个相同的标识符引入同一个程序,编译器或链接器将产生错误。此错误通常称为命名冲突(或命名冲突)。
上段例子引用自cpp-tutorial-2.8
下面再举一个实际函数中的例子
a.cpp
#include <iostream> void myFcn(int x) { std::cout << x; }
main.cpp
#include <iostream> void myFcn(int x) { std::cout << 2 * x; } int main() { return 0; }
编译器在编译该项目时,会独立的编译项目下的所有文件,所以两个cpp文件编译都没有问题
但是当链接器执行时,它会将a.cpp和main.cpp的所有定义链接在一起,此时就会发现函数myfunc的冲突定义,于是程序立即因为错误而终止
大多数命名冲突发生在两种情况下:
随着程序变大并使用更多标识符,引入命名冲突的几率显着增加。好消息是 C++ 提供了大量的机制来避免命名冲突。局部作用域,防止在函数内部定义的局部变量相互冲突,就是这样一种机制。但是局部作用域不适用于函数名称。那么我们如何避免函数名相互冲突呢?
这就引出了命名空间的概念啦~
如果您必须将邮件投递到两个地址,一个地址是 Mill City 的 Front Street 209,另一个地址是 Jonesville 的 Front Street 417,那么您将不会混淆该去哪里。换句话说,城市提供了分组,使我们能够消除可能相互冲突的地址的歧义。在这个类比中,命名空间就像城市一样。
命名空间:命名空间就是一个为了消除歧义而声明的局部作用域,在一个命名空间内声明的任何名称都不会被误认为是其他作用域中的名称
命名空间的用途:
命名空间通常用于对大型项目中的相关标识符进行分组,以帮助确保它们不会无意中与其他标识符发生冲突。例如,如果您将所有数学函数放在名为math的命名空间中,那么您的数学函数将不会与math命名空间之外的同名函数发生冲突。
在 C++ 中,任何未在类、函数或命名空间中定义的名称都被视为隐式定义的命名空间的一部分,称为全局命名空间(有时也称为全局作用域)。
在上面的示例中,函数 main() 和 myFcn() 的两个版本都在全局命名空间中定义。示例中遇到的命名冲突是因为 myFcn() 的两个版本最终都在全局命名空间内,这违反了命名空间中所有名称必须唯一的规则。
我们以c++的cout输出标识符为例,深入理解命名空间的含义
以下内容均转载自网址
在最初设计 C++ 时,C++ 标准库中的所有标识符(包括 std::cin 和 std::cout)都可以在没有std:: 的情况下使用前缀(它们是全局命名空间的一部分)。但是,这意味着标准库中的任何标识符都可能与您为自己的标识符选择的任何名称(也在全局命名空间中定义)发生潜在冲突。当您#include标准库中的新文件时,正在运行的代码可能会突然发生命名冲突。或者更糟的是,在一个 C++ 版本下编译的程序可能无法在 C++ 的未来版本下编译,因为引入标准库的新标识符可能与已经编写的代码存在命名冲突。因此,C++ 将标准库中的所有功能都移到了名为**“std”(standard 的缩写)的命名空间**中。
那么我们应该如何使用命名空间的标识符呢?
以c++中的cout基本输出函数为例
当我们说std::cout 时,我们说的是“存在于命名空间std 中的cout ”。
#include <iostream> int main() { std::cout << "Hello world!"; // when we say cout, we mean the cout defined in the std namespace return 0; }
直接引用了整个命名空间,此时当编译器区确定一个标识符cout究竟是什么时,它会在本地和std空间同时进行检索
很多教程都推荐这种方法,因为它非常方便快捷,但是这种方法的使用却违背了命名空间的初衷,因为命名空间和本地任然会对同一个cout产生冲突
#include <iostream> using namespace std; // this is a using directive telling the compiler to check the std namespace when resolving identifiers with no prefix int main() { cout << "Hello world!"; // cout has no prefix, so the compiler will check to see if cout is defined locally or in namespace std return 0; }
冲突
#include <iostream> // imports the declaration of std::cout using namespace std; // makes std::cout accessible as "cout" int cout() // declares our own "cout" function { return 5; } int main() { cout << "Hello, world!"; // Compile error! Which cout do we want here? The one in the std namespace or the one we defined above? return 0; }
上面的程序无法编译,因为编译器现在无法判断我们是想要我们定义的cout函数,还是在std命名空间内定义的cout。 当以这种方式使用 using 指令时,我们定义的任何标识符都可能与std命名空间中的任何同名标识符冲突。更糟糕的是,虽然今天的标识符名称可能不会发生冲突,但它可能会与未来语言修订版中添加到 std 命名空间的新标识符发生冲突,也就是说,同一个程序,在不同版本下的gcc标准可能不能运行,这是工程角度而言应该极力避免的东西,因为bug太难找了!这就是首先将标准库中的所有标识符移动到std命名空间的重点!
避免在程序顶部或头文件中使用指令(例如using namespace std;)。它们违反了当初添加名称空间的初衷。
但是在现在的打基础过程中,使用以下是可以的,但是到了真正的工程以及项目实践时,这种情况是完全不可取的!!!