我听说using namespace std;
是不好的做法,应该直接使用std::cout
和std::cin
。这是为什么呢?是否会冒险声明与 std
命名空间中某些东西同名的变量?
我听说using namespace std;
是不好的做法,应该直接使用std::cout
和std::cin
。这是为什么呢?是否会冒险声明与 std
命名空间中某些东西同名的变量?
有经验的程序员使用解决他们问题的方法,避免产生新问题,并且出于这个原因,他们会避免在头文件级别使用using-directives。
有经验的程序员也会尽量避免在源文件中完全限定名称。其中一个小原因是,在少量代码就足够时写更多的代码并不优雅 除非有好的原因。一个主要原因是关闭参数相关查找(ADL)。
那么这些 好的原因 是什么呢?有时候程序员明确想要关闭 ADL,其他时候他们想要消除歧义。
因此以下情况是可以接受的:
看到代码并知道它是干什么的感觉真好。如果我看到std::cout
,我知道那是std
库的cout
流。但如果我看到cout
,我就不知道了。它可以是std
库的cout
流。或者在同一个函数中前面的十行代码中有一个名为int cout = 0;
的变量。或者是该文件中命名为cout
的static
变量。它可能是任何东西。
现在考虑一个百万行代码库,这并不算特别大,你正在寻找一个错误,这意味着你知道这一百万行中有一行没有按照预期工作。例如,cout << 1;
可能读取了一个名为cout
的static int
,将其左移一位并丢弃结果。要查找错误,我必须检查这个。你能看出来吗?因此,我非常非常希望看到std::cout
。
这就是其中之一,如果你是一名教师,从未写过和维护过任何代码,就会觉得这是一个非常好的主意。我喜欢看到代码,(1) 我知道它是做什么的; (2) 我确信编写它的人知道它是做什么的。
我也认为这是一个不好的惯例。为什么?有一天我想,命名空间的作用是划分代码,所以我不应该把所有东西都扔进一个全局的袋子里。
然而,如果我经常使用'cout'和'cin',我会在.cpp文件中写:using std::cout; using std::cin;
(从不在头文件中写,因为它会随着#include
传播)。我认为没有一个理智的人会将一个流命名为cout
或cin
。 ;)
这都是关于管理复杂性的。使用命名空间会引入一些不需要的东西,因此可能会使调试变得更加困难(我说可能是因为情况视具体情况而定)。到处使用 std:: 会更难阅读(有更多文本等)。
因人而异 - 尽力以最好的方式管理您的复杂性。
一个具体的例子来澄清这个问题。想象一下,你有两个库,foo
和bar
,每个库都有自己的命名空间:
namespace foo {
void a(float) { /* Does something */ }
}
namespace bar {
...
}
foo
和bar
:using namespace foo;
using namespace bar;
void main() {
a(42);
}
目前为止一切都很好。当你运行程序时,它会“做某事”。但是后来你更新了bar
,假设它已经改变成如下:
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)
}
因此,您需要进行一些维护工作,以澄清'a'的意思为foo::a
。这是不可取的,但幸运的是,这很容易(只需在编译器标记为模糊的所有对a
的调用前面添加foo::
即可)。
但是,请想象另一种情况,bar发生了变化,变成了这样:
namespace bar {
void a(int) { /* Does something completely different */ }
}
a(42)
的调用突然绑定到bar::a
而不是foo::a
,并且不再执行“something”而是执行“something completely different”。没有编译器警告或其他提示。你的程序默默地开始执行与之前完全不同的操作。std
(由于该命名空间中的内容数量)感到更加不舒服。using
指令来指定它应该引入带有旧版本号标记的成员,但不包括那些带有新版本号标记的成员。如果在程序员编写using
指令时,最新版本的库是147,则程序将在使用指令中包含该版本号,以后添加的任何函数都会被标记为更高的数字,指定版本147的代码将继续像以前一样工作。 - supercat请考虑
// myHeader.h
#include <sstream>
using namespace std;
// someoneElses.cpp/h
#include "myHeader.h"
class stringstream { // Uh oh
};
请注意,这只是一个简单的例子。如果你有包含20个以上的文件和其他导入项,你将需要查找大量依赖项来找到问题所在。更糟糕的是,由于冲突定义,你可能会在其他模块中遇到不相关的错误。
这并不是非常可怕,但是如果你不在头文件或全局命名空间中使用它,你将节省自己很多麻烦。在非常有限的作用域中使用它可能没什么问题,但我从未因为要多打五个字符以明确函数来源而遇到过问题。
你需要能够阅读其他人所写的代码,即使这些人使用的编码风格和最佳实践不同于你。
如果只使用cout
,不会有人感到困惑。但当你在许多命名空间中穿梭,并看到某个类时,可能不确定它的作用是什么,显式指定命名空间就像是一种注释。你可以一眼看出,“噢,这是一个文件系统操作”或“那是在进行网络操作”。
std
并且只使用std
命名空间在我的看法中并不是太大的问题,因为重新定义只会发生在您自己的代码中...所以只需将它们视为保留名称的函数,如"int"或"class"即可。std::
命名空间中出现了像 min
、end
和 less
这样的短字符串。但是,现在 std::
中有数千个符号,对于读者来说,知道一个他们可能不知道的新符号来自哪里是很有用的。 - Tom Swirly命名空间是一种具有名称的范围。命名空间用于分组相关声明并将不同项保持分离。例如,两个独立开发的库可能使用相同的名称来引用不同的项,但用户仍然可以同时使用它们。
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
// ...
}
命名空间为不同的库和不同版本的代码管理提供了强大的工具。特别是,它们为程序员提供了在引用非本地名称时如何显式的选择。
来源:Bjarne Stroustrup 的 C++ 编程语言概述
我同意其他人的看法,但我希望解决有关可读性的问题 - 您可以通过在文件、函数或类声明顶部简单地使用typedef来避免所有这些问题。
通常我会在类声明中使用它,因为类中的方法往往处理相似的数据类型(成员变量),而typedef是一个机会,可以分配一个在类上下文中有意义的名称。这实际上有助于定义类的方法时的可读性。
// Header
class File
{
typedef std::vector<std::string> Lines;
Lines ReadLines();
}
在实施过程中:
// .cpp
Lines File::ReadLines()
{
Lines lines;
// Get them...
return lines;
}
相对于:
// .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;
}
using
命令,除非针对特定的命名空间,例如std::literals::chrono_literals
、Poco::Data:Keywords
、Poco::Units
等与字面值或可读性技巧有关的内容。无论是在头文件还是实现文件中都不应该使用。在函数作用域内可能还可以接受,但除了字面值和相关内容外都没有用处。 - Ludovic Zenohate Lagouardette