避免使用using namespace std;的原因详解

在这里,我毫不回避地说了这句话:
引用
我再也不想在任何头文件中看到“using namespace xxx;”了

作为一个开发者/团队领导者,我经常会去招聘新的项目成员,有时候也帮助其他组的人来面试应聘者。作为应聘流程之一,我经常要求应聘者写一些代码,因此我检查过相当多的代码。在最近提交的C++代码中,我注意到一个趋势,在任何头文件中,我总是能看到以下代码:

C++代码
using namespace std;

如果我用我们的代码检查系统(在实践中我十分推荐这个系统)来检验代码,以上那行代码经常会跟着一句评论“Timo不会这样写的”。他们说得很对,我确实不会这么写。

那么,为什么我说服了很多C++教材(也许并不是什么好事),让他们认为使用上面那段代码是非常坏的方式?

让我们先来看看上面那段代码做了什么。总的来说,它把命名空间“std”以内的所有内容(或者其他由作者用using调用命名空间)无一例外的引入了目前的命名空间中。请注意我说的“所有内容”,并不是一两个你想用的类\类型\模板。在一段代码的开头引入命名空间的原因则是加强程序模块化,和减少命名冲突。大体上,它允许你可以写类似下面的那段代码,并且保证编译器可以选择正确的实现:

C++代码
std::vectorstd::string names;
my_cool_reimplementation::vector<our_internal_stuff::string> othernames;

现在,假定我们正在尝试减少代码输入,并且在以上代码中使用using声明(或者更糟糕的,两个命名空间都声明了),按照如下代码来实现:

C++代码
vector names;
vector<our_internal_stuff::string> othernames;

如果这段代码的作者很幸运的话,编译器会选择vector的正确实现,或者至少在最初的阶段会这么做。但是过了一段时间,你会碰到一些很奇怪的编译器错误。幸运的话,你能找到这些错误的原因——我曾经遇到过类似问题,我花费了好几天才能找到这类问题的原因。该死,它们会浪费你很多的时间,仅仅因为你为了想少打5个字符的代码。

并且,如果你把using声明用在了头文件中,你会让这类问题更加恶化,因为命名冲突问题早晚都会在一个调用关系非常非常远的模块中神不知鬼不觉的出现,而你可能需要查三层调用才可以找到原因所在,一个头文件包含了另一个直接使用using声明的头文件可以导致命名空间被立刻污染掉,任何一个使用命名空间的文件如果使用了std命名空间的内容,都会导致这类问题。

那么,为什么你能在很多教科书中看到它们使用using namespace std?我的理论是,它确实会帮助改善整本书的排版,并且能减少一些视觉的混乱。在一本纸质书中,你只有很有限的空间来写文字,因此你必须最大限度的利用它,加之书中的代码例子通常都很简单。但另一方面,不同的命名空间限定符会带来了很多视觉混乱,这会让读者很难从上下文判断作者的意图。当你想在这个时代写一些有效率的代码的时候,以上两点都不完全对,现在的编译器大多数能每行处理60-80个单词(你可以试试,这可以的)。因此,不要乱引入命名空间。

如果你非常明确的想在一个头文件中使用using声明,应该怎么做?我们有其他途径可以减少不得不用using声明的情况——你可以用以下其中一种,或则多种方式的组合。

首先,你只需使用typedef。我会建议你使用这种方法,即使我并不经常遵循我自己的建议,我也认为无论如何这都是一个在实际应用中很好的方法。实际上,使用typedef有两个好处——他让一个类型名可读性增加,如果你选择了一个很好的名字,它可以让作者的意图更加显而易见。比较一下如下的声明方式:

C++代码
std::map<std::string, long> clientLocations;
typedef std::map<std::string, long> ClientNameToZip;
ClientNameToZip clientLocations;

第二个声明——即使它被展开为两行——也比第一个声明更加直观,同时,它也避免了命名空间模糊化。

另外一个选择则是用两种方法来限制using声明的作用域——仅仅是你想用的那个“using”符号,例如:

C++代码
using std::string;

但是,把这段声明扔到头文件中,几乎和使用“using namespace”一样糟糕,因此,你应该使用作用域来限制下它的可见性,来确保你的using声明真的只在第一次做using声明的地方有效。例如,你可以用如下方法限制类声明作用域:

C++代码
namespace bar
{
struct zzz
{

};
}
class foo
{
using namespace bar;
zzz m_snooze; // Pulls in bar::zzz
};

或者,你可以直接把using的作用域限制到一个函数中,例如:

C++代码
void temp()
{
using namespace std;
string test = “fooBar”;
}

不管哪种方法,你都可以把using的作用域限制到需要使用它的代码中,而不是把它放到代码的公共空间中。你的工程越大,确保模块化,和最小化不可预料的负面影响就显得越发重要。
因为使用STL中 有部分名称是没有加下划线的保留标记的
而在自己的源代码中用到了后会引发未定义的后果

例如:
#include
using namespace std;
int main()
{
int max=0;
}

一直我都告诫学习C++的人 以后精通了C++就不要用using namespace std;了
用例如using std::cin;效果会更好

举个血淋淋的例子
在NOIP2014提高组比赛时 有个朋友因为用到了计数器 就用了count (全局声明using namespace std;)
本地编译(Dev-C++)通过 测试一切正常
但在提交答案之后 走出考场 谈论比赛过程时无意中提到count
我很惊讶 为何能编译通过 我以前在学校训练时用的C-free声明using namespace std;时用count会报错指出冲突
所以从那时候开始我在做题时就没有用过using编译指令 只用using声明
他一拍脑袋说忘了这个茬了但是后悔已经晚了
他那题由于编译未通过 0分只拿到了省三

----update:Jun 18th , 2015

看来论据不够充分 那我加下

《C++ Primer Plus (第六版 中文版 人民邮电出版社)》第九章:内存模型和名称空间 第328页:
“有关using编译命令和using声明,需要记住的一点是,他们增加了名称冲突的可能性。”

《C++ Primer Plus (第六版 中文版 人民邮电出版社)》第九章:内存模型和名称空间 第329页:
一般说来,使用using命令比使用using编译命令更安全,这是由于它只导入了制定的名称。如果该名称与局部名称发生冲突,编译器将发出指示。using编译命令导入所有的名称,包括可能并不需要的名称。如果与局部名称发生冲突,则局部名称将覆盖名称空间版本,而编译器并不会发出警告。另外,名称空间的开放性意味着名称空间的名称可能分散在多个地方,这使得难以准确知道添加了哪些名称。

然而名称空间的支持者希望有更多的选择,既可以使用解析运算符面也可以使用using声明,也就是说,不要这样做:
using namespace std; // avoid as too indiscriminate(随意)
而应这样做
int x;
std::cin >> x ;
std::cout << x << std::endl;
或者这样做
using std::cin;
using std::cout;
using std::endl;
int x;
cin >> x;
cout << x << endl;

《C++ Primer Plus (第六版 中文版 人民邮电出版社)》附录I: 转换为ISO标准的C++ 第915页:
名称空间有助于组织程序中使用标识符,避免名称冲突。由于标准库是使用性的头文件组织实现的,它将名称放在std名称空间中,因此使用这些头文件需要处理名称空间。
出于简化的目的,本书的事例通常使用编译命令using来使std名称空间中名称可用:
#include
#include
#include
using namespace std; //a using-directive
然而,不管需要于否,都导出名称空间中的所有名称,是于名称空间的初衷背道而驰的。

由此可见 using namespace std; 并不是可以随意使用的编译命令。

----update:Jun 18th , 2015 night.

觉得例子还不够丰富 就上了google看看
在Steve Donovan 《C++ by Example》中写到:
However, some people feel strongly that using namespace std cases namespace pollution becauseeverything is dumped into the global namespace,which is what namespaces were designed to prevent.You need to understand the implications of using namespace std,and you need to recognize that there is one case where italways a bad idea.

而在国外的论坛StackOverflow
对于What requires me to declare “using namespace std;”?
Péter Török给出了这样的解释
However,
using namespace std;
is considered a bad practice because you are practically importing the whole standard namespace,thus opening up a lot of possibilities for name clashes. It is better to import only the stuff you are actually using in your code, like
using std::string;

猜你喜欢

转载自blog.csdn.net/weixin_44489823/article/details/89790283