为什么“使用命名空间标准”被认为是不好的做法?
其他人已经多次告诉我,我的老师在代码中using namespace std
的build议是错误的。 因此,我们应该使用std::cout
和std::cin
。
为什么using namespace std
被认为是不好的做法? 这是真的低效率或风险声明不明确的variables(variables与std
命名空间中的函数共享相同的名称)? 或者,这是否会影响性能?
这根本不关系到性能。 但是考虑一下:你正在使用两个名为Foo和Bar的库:
using namespace foo; using namespace bar;
一切正常,你可以从Foo和Quux()
从Bar中调用Blah()
而没有任何问题。 但有一天你升级到新版本的Foo 2.0,它现在提供了一个叫做Quux()
的函数。 现在你遇到了一个冲突:Foo 2.0和Bar将Quux()
导入到你的全局命名空间中。 这将需要一些努力来解决,特别是如果function参数匹配。
如果你使用了foo::Blah()
和bar::Quux()
,那么foo::Quux()
的引入将是一个非事件。
我同意格雷格写的所有内容,但是我想补充一点: 它甚至可能比格雷格说的更糟糕!
Library Foo 2.0可能会引入一个函数 Quux()
,这个函数与你的代码调用Quux()
相比,是一个明确的更好的匹配 。 然后你的代码仍然编译 ,但它默默地调用错误的function ,并知道什么。 事情可能会变得糟糕。
请记住, std
名称空间有很多标识符,其中很多是非常常见的标识符(认为list
, sort
, string
, iterator
等),这些代码很可能出现在其他代码中。
如果你认为这是不太可能的: 有一个问题在Stack Overflow上提到,在我给出这个答案后的大约半年时间里发生了这个事情(由于省略了std::
前缀而调用了错误的函数)。 这是另外一个这样的问题最近的例子 。 所以这是一个真正的问题 。
这里还有一个数据点:很多很多年以前,我也习惯于把标准库中的所有东西都用std::
加上前缀。 然后我在一个项目中工作,在这个项目中,一开始就决定除了函数作用域之外,都禁止using
指令和声明。 你猜怎么了? 我们大部分时间都习惯于编写前缀,而且在几周之后,我们大部分人甚至认为它确实使代码更具可读性 。 这是有原因的: 无论你喜欢更短或更长的散文是主观的,但前缀客观地增加了清晰的代码。 不仅编译器,而且你也更容易看到哪个标识符被引用。
十年来,这个项目已经有了几百万行的代码。 由于这些讨论一次又一次地出现,我曾经很好奇在项目中实际使用(允许的)函数范围的频率。 我对它的来源进行了处理,只发现了一两个地方。 对我来说,这表明一旦尝试过,开发人员 即使在允许使用指令的情况下也不会发现std::
painful 足够使用指令。
底线:明确地加上前缀并不会造成任何伤害,只需要很less的习惯,并具有客观的优势。 特别是,它使代码更容易被编译器和读者解读 – 而这应该是编写代码的主要目标。
我认为把它放在你的类的头文件中是不好的,因为那样你就会迫使任何希望使用你的类的人(通过包含你的头文件)也是“使用”(即看到所有内容)这些其他的名字空间。
但是,您可以随意在您的(私有)* .cpp文件中使用使用语句。
注意有人不同意我这样的说法:“感觉自由”就是这样 – 因为尽pipecpp文件中的using语句比header中的更好 (因为它不会影响包含头文件的人),但他们认为它仍然是不好 (因为根据代码,可能会使课程的实施更难以维护)。 这个常见问题主题说,
using-directive存在于传统的C ++代码中,并且可以简化向命名空间的转换,但是您可能不应该定期使用它,至less不要在新的C ++代码中使用它。
它提出了两种select:
-
使用声明:
using std::cout; // a using-declaration lets you use cout without qualification cout << "Values:";
-
克服它,只需inputstd ::
std::cout << "Values:";
最近我遇到了有关Visual Studio 2010的投诉。 事实certificate,几乎所有的源文件都有这两行:
using namespace std; using namespace boost;
很多Boost特性都进入了C ++ 0x标准,而Visual Studio 2010有很多C ++ 0x特性,所以这些程序突然间没有被编译。
因此,避免using namespace X;
是面向未来的一种forms,确保对正在使用的库和/或头文件进行更改的方法不会破坏程序。
短版本:不要在头文件中使用全局使用声明或指令。 随意在实现文件中使用它们。 以下是Herb Sutter和Andrei Alexandrescu在C ++编码标准中对这个问题所要说的(对于强调,这个粗体是我的):
概要
命名空间的使用是为了您的方便,而不是为了给别人造成伤害:不要在#include指令前写一个使用声明或使用指令。
推论:在头文件中,不要使用命令或使用声明来编写命名空间级的命令; 相反,明确命名空间 – 限定所有名称。 (第一条规则是第二条规则,因为标题永远不会知道其他标题#includes后面会出现什么内容)。
讨论
简而言之:在#include指令之后,您可以并且应该在您的实现文件中宽泛地使用声明和指令来使用名称空间,并且感觉良好。 尽pipe反复声明,使用声明和指令的命名空间并不是邪恶的,它们并没有打败命名空间的目的。 相反,它们是使命名空间可用的 。
不应该在全局范围使用指令,特别是在头文件中。 但是,即使在头文件中也有适合的情况:
template <typename FloatType> inline FloatType compute_something(FloatType x) { using namespace std; //no problem since scope is limited return exp(x) * (sin(x) - cos(x * 2) + sin(x * 3) - cos(x * 4)); }
这比明确的限定( std::sin
, std::cos
…)更好,因为它更短,并且能够使用用户定义的浮点types(通过参数相关查找)。
不要全局使用它
只有在全球使用时才被认为是“不好的”。 因为:
- 你混淆了你正在编程的名字空间。
- 读者将很难看到一个特定的标识符来自何处,当你使用许多
using namespace xyz
。 - 无论对于其他源代码读者来说,对于最频繁的读者来说,更是如此:你自己。 回来一两年,看看…
- 如果你只谈论
using namespace std
你可能不知道所有你抓的东西 – 当你添加另一个#include
或移动到一个新的C ++修订版本时,你可能会得到你不知道的名称冲突。
你可以在本地使用它
继续在本地(几乎)自由使用它。 这当然会阻止你重复std::
– 而重复也是不好的。
在本地使用它的一个成语
在C ++ 03中有一个成语 – 样板代码 – 用于为类实现swap
function。 有人build议,你实际上使用本地using namespace std
– 或者至lessusing 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_
selectstd::swap
,即void std::swap(int, int)
。 - 如果你有一个重载
void swap(Child&, Child&)
,编译器会select它。 - 如果你没有那个超载的话,编译器将会使用
void std::swap(Child&,Child&)
并且尽可能地交换这些。
使用C ++ 11,没有理由再使用这种模式。 std::swap
的执行被改变,以find一个潜在的过载,并select它。
如果你导入了正确的头文件,你的全局范围中突然有了像hex
, left
, plus
或count
这样的名字。 如果您不知道std::
包含这些名称,这可能会令人惊讶。 如果您也尝试在本地使用这些名称,可能会导致相当混乱。
如果所有标准的东西都在自己的命名空间中,则不必担心与代码或其他库的名称冲突。
有经验的程序员使用任何解决他们的问题,并避免任何造成新问题。 因此,出于显而易见的原因,他们避免了头文件级的使用指令。
他们尽量避免在源文件中完全限定名称。 一个小问题是,如果没有充分的理由 ,编写更多的代码就不够优雅。 关键在于closuresADL。
这些好理由是什么? 有时你明确地要closuresADL。 有时候你想消除歧义。
所以以下是确定的:
- 在函数的实现中使用函数级的使用指令和使用声明
- 源文件中的源文件级使用声明
- (有时)源文件级使用指令
我同意它不应该在全球范围内使用,但是在本地使用并不是那么坏,就像在namespace
。 下面是“C ++编程语言”的一个例子:
namespace My_lib { using namespace His_lib; // everything from His_lib using namespace Her_lib; // everything from Her_lib using His_lib::String; // resolve potential clash in favor of His_lib using Her_lib::Vector; // resolve potential clash in favor of Her_lib }
在这个例子中,我们解决了潜在的名称冲突和由其构成引起的模糊性。
在那里明确声明的名字(包括使用声明(比如His_lib::String
)声明的名字)优先于通过using-directive( using namespace Her_lib
)在另一个作用域中可访问的using namespace Her_lib
。
另一个原因是惊喜。
如果我看到cout << blah
,而不是std::cout << blah
我认为这是什么“cout”? 这是正常的吗? 这是特别的吗?
我也认为这是一个不好的做法。 为什么? 就在有一天,我认为命名空间的function就是分解东西,所以我不应该把所有东西都放到一个全局包里。 但是,如果我经常使用'cout'和'cin',我写: using std::cout; using std::cin;
using std::cout; using std::cin;
在cpp文件中(从不在头文件中,因为它使用#include
传播)。 我认为没有人会理智的说出一个streamcout
或cin
。 ;)
这完全是关于pipe理复杂性。 使用命名空间将拉动你不想要的东西,从而可能使debugging更困难(我可能说)。 在这个地方使用std ::很难阅读(更多的文字和所有的)。
马的课程 – pipe理你的复杂性,你最好的可以和感觉能够。
-
你需要能够阅读比你有不同风格和最佳实践意见的人写的代码。
-
如果你只使用cout,没有人会感到困惑。 但是,当你有很多命名空间飞来飞去,你看到这个类,你不确定它做了什么,明确命名空间作为一个sorting的评论。 你可以乍一看,'哦,这是一个文件系统操作'或'那是做networking的东西'。
在同一时间使用许多命名空间显然是一个灾难的秘诀,但使用JUST命名空间std
,只有命名空间std
是不是在我看来是一个大交易,因为重定义只能通过你自己的代码发生…
所以只要考虑它们的function,如“int”或“class”的保留名称就是这样。
人们应该停止这样的肛门。 你的老师一直都是对的。 只要使用一个命名空间; 那就是使用命名空间的首要地位。 你不应该同时使用多个。 除非是你自己的。 所以重新定义也不会发生。
考虑
// myHeader.h #include <sstream> using namespace std; // someoneElses.cpp/h #include "myHeader.h" class stringstream { // uh oh };
请注意,这是一个简单的例子,如果你有20包含和其他import的文件,你将有大量的依赖关系来解决问题。 更糟糕的是,您可以根据冲突的定义在其他模块中获取不相关的错误。
这并不可怕,但是通过不在头文件或全局名称空间中使用它,您可以节省自己的头痛。 在非常有限的范围内完成它可能是可以的,但我从来没有遇到过input额外5个字符的问题,以澄清我的function来自哪里。
很高兴看到代码,并知道它做了什么。 如果我看到“std :: cout”,我知道:这是std库的coutstream。 如果我看到“cout”,那么我不知道。 它可能是std库的coutstream。 或者可能有一个“int cout = 0;” 同一function高十行。 或者在该文件中名为cout的静态variables。 这可能是任何事情。
现在需要一百万行的代码库,这不是特别大,而且你正在寻找一个bug,这意味着你知道在这一百万行中有一行没有做它应该做的事。 “cout << 1;” 可以读取一个名为cout的静态int,将它左移一位,并丢弃结果。 寻找一个错误,我不得不检查。 你能看到我真的很喜欢看“std :: cout”吗?
如果你是一位老师,那么这些东西似乎是一个非常好的主意,而且从来不必为了生活而编写和维护任何代码。 我喜欢看代码在哪里(1)我知道它做什么和(2)我相信写它的人知道它做了什么。
一个具体的例子来澄清关心。 想象一下,你有一个情况,你有两个图书馆,富和酒吧,每个都有自己的命名空间:
namespace foo { void a(float) { /* does something */ } } namespace bar { ... }
现在让我们假设你在你自己的程序中一起使用foo和bar,如下所示:
using namespace foo; using namespace bar; void main() { a(42); }
在这一点上一切都很好。 当你运行你的程序时,它会“做一些事情”。 但后来你更新吧,并且让我们说它已经改变为:
namespace bar { void a(float) { /* does something completely different */ } }
在这一点上,你会得到一个编译器错误:
using namespace foo; using namespace bar; void main() { a(42); // error: call to 'a' is ambiguous, should be foo::a(42) }
所以你需要做一些维护来澄清你的意思(即foo::a
)。 这可能是不受欢迎的,但幸运的是它非常简单(只需在所有调用之前添加foo::
以使编译器标记为不明确)。
但是想象一下,在一个替代的情况下,酒吧改变,而不是像这样:
namespace bar { void a(int) { /* does something completely different */ } }
在这一点上,你对a(42)
调用突然绑定到bar::a
而不是foo::a
,而不是做一些“完全不同的东西”。 无编译器警告或任何东西。 你的程序只是默默地开始做与以前完全不同的事情。
当你使用命名空间时,你冒着这样的风险,这就是为什么人们使用命名空间不舒服的原因。 命名空间中的东西越多,冲突的风险就越大,所以使用命名空间std(由于该命名空间中的东西的数量)比其他命名空间更令人不舒服。
最终这是可写性与可靠性/可维护性之间的折衷。 可读性也可能是因素,但我可以看到任何方式的争论。 通常我会说可靠性和可维护性更重要,但在这种情况下,您将不断支付可写性成本,以实现相当less的可靠性/可维护性影响。 “最好”的权衡将决定你的项目和你的优先事项。
名称空间是一个命名的作用域。 命名空间用于分组相关的声明,并将单独的项目分开。 例如,两个单独开发的库可能会使用相同的名称来引用不同的项目,但用户仍然可以同时使用两个:
namespace Mylib{ template<class T> class Stack{ /* ... */ }; / / ... } namespace Yourlib{ class Stack{ /* ... */ }; / / ... } void f(int max) { Mylib: :Stack<int> s1(max) ; / / use my stack Yourlib: :Stack s2(max) ; / / use your stack / / ... }
重复命名空间名称可能会使读者和作者分心。 因此,可以声明来自特定名称空间的名称没有明确的限定条件。 例如:
void f(int max) { using namespace Mylib; / / make names from Mylib accessible Stack<int> s1(max) ; / / use my stack Yourlib: :Stack s2(max) ; / / use your stack / / ... }
Namespaces provide a powerful tool for the management of different libraries and of different versions of code. In particular, they offer the programmer alternatives of how explicit to make a reference to a nonlocal name.
Source : An Overview of the C++ Programming Language by Bjarne Stroustrup
I agree with the others here, but would like to address the concerns regarding readability – you can avoid all of that by simply using typedefs at the top of your file, function or class declaration.
I usually use it in my class declaration as methods in a class tend to deal with similar data types (the members) and a typedef is an opportunity to assign a name that is meaningful in the context of the class. This actually aids readability in the definitions of the class methods.
//header class File { typedef std::vector<std::string> Lines; Lines ReadLines(); }
and in the implementation:
//cpp Lines File::ReadLines() { Lines lines; //get them... return lines; }
as opposed to:
//cpp vector<string> File::ReadLines() { vector<string> lines; //get them... return lines; }
要么:
//cpp std::vector<std::string> File::ReadLines() { std::vector<std::string> lines; //get them... return lines; }
An example where using namespace std throws complilation error because of the ambiguity of count, which is also a function in algorithm library.
#include <iostream> using namespace std; int count = 1; int main() { cout<<count<<endl; }
I do not think it is necessarily bad practice under all conditions, but you need to be careful when you use it. If you're writing a library, you probably should use the scope resolution operators with the namespace to keep your library from butting heads with other libraries. For application level code, I don't see anything wrong with it.
"Why is 'using namespace std;' considered a bad practice in C++?"
I put it the other way around: Why is typing 5 extra chars is considered cumbersome by some?
Consider eg writing a piece of numerical software, why would I even consider polluting my global namespace by cutting general "std::vector" down to "vector" when "vector" is one of the problem domain's most important concepts?
With unqualified imported identifiers you need external search tools like grep to find out where identifiers are declared. This makes reasoning about program correctness harder.
To answer your question I look at it this way practically: a lot of programmers (not all) invoke namespace std. Therefore one should be in the habit of NOT using things that impinge or use the same names as what is in the namespace std. That is a great deal granted, but not so much compared to the number of possible coherent words and pseudonyms that can be come up with strictly speaking.
I mean really… saying "don't rely on this being present" is just setting you up to rely on it NOT being present. You are constantly going to have issues borrowing code snippets and constantly repairing them. Just keep your user-defined and borrowed stuff in limited scope as they should be and be VERY sparing with globals (honestly globals should almost always be a last resort for purposes of "compile now, sanity later"). Truly I think it is bad advice from your teacher because using std will work for both "cout" and "std::cout" but NOT using std will only work for "std::cout". You will not always be fortunate enough to write all your own code.
NOTE: Don't focus too much on efficiency issues until you actually learn a little about how compilers work. With a little experience coding you don't have to learn that much about them before you realize how much they are able to generalize good code into something something simple. Every bit as simple as if you wrote the whole thing in C. Good code is only as complex as it needs to be.
I agree with others – it is asking for name clashes, ambiguities and then the fact is it is less explicit. While I can see the use of using … my personal preference is to limit it. I would also strongly consider what some others pointed out:
If you want to find a function name that might be a fairly common name, but you only want to find it in std namespace (or the reverse: you want to change all calls that are NOT in namespace std, namespace X, …), then how do you propose to do this? You could write a program to do it but wouldn't it be better to spend time working on your project itself rather than writing a program to maintain your project?
Personally I actually don't mind the std:: prefix. I like the look more than not. I don't know if that is because it is explicit and says to me "this isn't my code… I am using the standard library" or if it is something else, but I think it looks nicer. This might be odd given that I only recently got in to C++ (used and still do C and other languages for much longer and C is my favourite language of all time, right above assembly).
There is one other thing although it is somewhat related to the above and what others point out. While this might be bad practise, I sometimes reserve std::name for standard library version and name for program-specific implementation. Yes indeed this could bite you and bite you hard but it all comes down to that I started this project from scratch and I'm the only programmer for it. Example: I overload std::string and call it string. I have helpful additions. I did it in part because of my C and Unix (+ Linux) tendency towards lower-case names.
Besides that, you can have namespace aliases. Here is an example of where it is useful that might not have been referred to (I didn't read all the responses and I'm having to rush off for a while in a moment). I use the C++11 standard and specifically with libstdc++. Well check this. It doesn't have complete std::regex support. Sure it compiles but it throws an exception along the lines of it being an error on the programmer's end. But it is lack of implementation. So here's how I solved it. Install boost's regex, link in boost's regex. Then, I do the following so that when libstdc++ has it implemented entirely, I need only remove this block and the code remains the same:
namespace std { using boost::regex; using boost::regex_error; using boost::regex_replace; using boost::regex_search; using boost::regex_match; using boost::smatch; namespace regex_constants = boost::regex_constants; }
I won't argue on whether that is a bad idea or not. I will however argue that it keeps it clean for MY project and at the same time makes it specific: True I have to use boost BUT I'm using it like the libstdc++ will eventually have it. Yes, starting your own project and starting with a standard (…) at the very beginning goes a very long way with helping maintenance, development and everything involved with the project!
Edit: Now that I have time, just to clarify something. I don't actually think it is a good idea to use a name of a class/whatever in the STL deliberately and more specifically in place of. string is the exception (ignore the first, above, or second here, pun if you must) for me as I didn't like the idea of 'String'. As it is, I am still very biased towards C and biased against C++. Sparing details, much of what I work on fits C more (but it was a good exercise and a good way to make myself a. learn another language and b. try not be less biased against object/classes/etc which is maybe better stated as: less closed-minded, less arrogant, more accepting.). But what IS useful is what some already suggested: I do indeed use list (it is fairly generic, is it not ?), sort (same thing) to name two that would cause a name clash if I were to do "using namespace std;" and so to that end I prefer being specific, in control and knowing that if I intend it to be the standard use then I will have to specify it. Put simply: no assuming allowed.
And as for making boost's regex part of std. I do that for future integration and – again, I admit fully this is bias – I don't think it is as ugly as boost::regex:: … Indeed that is another thing for me. There's many things in C++ that I still have yet to come to fully accept in looks and methods (another example: variadic templates versus var args [though I admit variadic templates are very very useful!]). Even those that I do accept it was difficult AND I still have issues with them.
It depends on where it is located. If it is a common header, then you are diminishing the value of the namespace by merging it into the global namespace. Keep in mind, this could be a neat way of making module globals.
From my experiences, if you have multiple libraries that uses say, cout
, but for a different purpose you may use the wrong cout
.
For example, if I type in, using namespace std;
and using namespace otherlib;
and type just cout (which happens to be in both), rather than std::cout
(or 'otherlib::cout'
), you might use the wrong one, and get errors, it's much more effective and efficient to use std::cout
.
I think using locally or globally should depend on the application.
Because, when we use the library locally, sometimes code going to be a real mess. Readability is going to low.
so, we should use libraries locally when only there is a possibility for conflicts.
I am not more experiences person. So, let me know if I am wrong.
Yes, the namespace is important. Once in my project, I needed to import one var declaration into my source code, but when compiling it, it conflicted with another third-party library.
At the end, I had to work around around it by some other means and make the code less clear.