我听说过使用名称空间std;这是不好的做法,我应该直接使用std::cout和std::cin。这是为什么?声明与std命名空间中的某个变量同名的变量是否有风险?
当前回答
另一个原因是惊讶。
如果我看到cout<<blah,而不是std::cout<<blah,我会想:这是什么cout?这是正常的服装吗?有什么特别的吗?
其他回答
考虑
// myHeader.h
#include <sstream>
using namespace std;
// someoneElses.cpp/h
#include "myHeader.h"
class stringstream { // Uh oh
};
请注意,这是一个简单的示例。如果您有包含20个include和其他导入的文件,那么您将有大量的依赖项来解决问题。更糟糕的是,根据冲突的定义,在其他模块中可能会出现不相关的错误。
这并不可怕,但如果不在头文件或全局名称空间中使用它,您将省去麻烦。在非常有限的范围内执行它可能是正确的,但我从未遇到过键入额外的五个字符来澄清我的函数来自何处的问题。
有经验的程序员使用任何解决他们问题的方法,避免任何产生新问题的方法。出于这个确切的原因,他们避免使用头文件级别的指令。
经验丰富的程序员也尽量避免在源文件中使用完全限定的名称。这样做的一个次要原因是,除非有充分的理由,否则在代码较少的情况下编写更多的代码是不优雅的。一个主要原因是关闭参数依赖查找(ADL)。
这些好理由是什么?有时程序员明确希望关闭ADL,有时他们希望消除歧义。
因此,以下内容是可以的:
函数级使用指令并在函数的实现中使用声明使用源文件内声明的源文件级别(有时)使用指令的源文件级别
这可能比Greg写的更糟!
Library Foo 2.0可能会引入一个函数Qux(),它无疑比多年来代码调用的bar::Qux)更适合您对Qux的某些调用。然后你的代码仍然可以编译,但它会默默地调用错误的函数,上帝知道会发生什么。这是最糟糕的事情了。
请记住,std名称空间有大量的标识符,其中许多是非常常见的标识符(例如列表、排序、字符串、迭代器等),这些标识符也很可能出现在其他代码中。
如果你认为这不太可能:在我给出这个答案大约半年后,在Stack Overflow上有一个问题(由于省略了std::前缀而调用了错误的函数)。这是这一问题的另一个最近的例子。所以这是一个真正的问题。
这里还有一个数据点:很多很多年前,我还发现必须在标准库中的所有内容前面加上std::,这很烦人。然后我在一个项目中工作,一开始就决定禁止使用指令和声明,但函数作用域除外。猜猜怎么了?我们中的大多数人花了几周的时间才习惯写前缀,再过几周,我们中的大部分人甚至同意这实际上使代码更可读。这是有原因的:你喜欢较短或较长的散文是主观的,但前缀客观上增加了代码的清晰度。不仅是编译器,您也会发现更容易看到引用的是哪个标识符。
在十年内,该项目发展到拥有数百万行代码。由于这些讨论一次又一次出现,我一度很好奇项目中实际使用(允许)函数范围的频率。我查阅了它的来源,只找到了一两打使用它的地方。对我来说,这表明,一旦尝试过,即使在允许使用std::的地方,开发人员也不会觉得每100kLoC使用一次指令就足够痛苦。
一句话:明确地给每件事加上前缀不会造成任何伤害,只需要很少的时间就可以习惯,而且有客观的优势。特别是,它使编译器和人类读者更容易解释代码,这可能是编写代码时的主要目标。
不要全局使用它
只有在全球范围内使用时,它才被认为是“坏的”。因为:
你把正在编程的命名空间弄得乱七八糟。当您使用许多名称空间xyz;时,读者将很难看到特定标识符的来源;。无论对你的源代码的其他读者来说是什么,对最经常阅读源代码的读者来说更是如此:你自己。一两年后回来看看。。。如果您只讨论使用名称空间std;你可能不知道你抓取的所有内容——当你添加另一个#include或移动到一个新的C++修订版时,你可能会遇到你不知道的名称冲突。
您可以在本地使用
继续,在本地(几乎)自由使用它。当然,这可以防止你重复std::--,重复也是不好的。
本地使用它的习惯用法
在C++03中,有一种习惯用法——样板代码——用于为类实现交换函数。有人建议您实际使用本地using命名空间std;--或至少使用std::swap;:
class Thing {
int value_;
Child child_;
public:
// ...
friend void swap(Thing &a, Thing &b);
};
void swap(Thing &a, Thing &b) {
using namespace std; // make `std::swap` available
// swap all members
swap(a.value_, b.value_); // `std::stwap(int, int)`
swap(a.child_, b.child_); // `swap(Child&,Child&)` or `std::swap(...)`
}
这有以下魔力:
编译器将为value_选择std::swap,即void std::swap(int,int)。如果实现了重载void swap(Child&,Child&),编译器将选择它。如果没有重载,编译器将使用void std::swap(Child&,Child&),并尽可能地交换这些。
对于C++11,没有理由再使用这种模式了。对std::swap的实现进行了更改,以找到一个潜在的过载并选择它。
我不认为这在任何情况下都是不好的做法,但在使用它时都需要小心。如果你正在编写一个库,你可能应该在命名空间中使用作用域解析运算符,以防止你的库与其他库冲突。对于应用程序级代码,我看不出它有什么问题。