使用namespace std与其他替代方案

4
using namespace std;

到目前为止,在我的计算机科学课程中,这是我们被告知要做的所有事情。不仅如此,否则我们的代码会受到惩罚,这也是我们被允许做的所有事情。通过查看在线发布的代码,我了解到您可以使用::std或std::来实现相同的功能。
我的问题通常是为什么?显然,为了学习者和简单起见,使用全局声明更简单,但是有什么缺点吗?在真实世界的应用程序中,使用::std更加现实吗?我想补充一下,使用声明背后的逻辑/概念是什么?在我的课程中没有解释过这些,我想更好地掌握它们。
作为一个普遍的问题:如果我没有学习这些内容,如向量、模板、类或错误处理,那么似乎我错过了很多必要的C++功能?
提前感谢!

2
我建议花点时间阅读这个问题及其各种回答。一个杰出的例子是,在某个翻译单元中无意中使用using namespace std;,导致了意想不到的后果,可以在这里看到。值得一读。 - WhozCraig
1
滥用 using namespace ... 是我个人认为的不良实践,可能会导致许多微妙且难以发现的错误。它绕过了引入命名空间的整个原因。尽管如此,当其范围受到限制(通过命名空间或本地作用域)时,using namespace ... 也有适当的用途。即便如此,std 命名空间非常庞大,包含许多常见符号,因此我总是避免使用 using namespace std另请参阅:C++ FAQ - Galik
2
但是我应该补充一点,在课堂上要听从你的计算机科学教授的指导,但不要把任何一个人告诉你的话当作圣经般地接受。学习最佳实践是一个持续的过程,你会从许多不同的人那里学到它们,这需要很多年的时间。(即使那时人们仍然会争论其中的一些问题,呵呵)。 - Galik
是的,我想这主要是我想要的。我只是想尽可能做好准备。而且,我会尝试保持开放的心态,考虑到她告诉我们始终选择数组而不是向量的教学风格。 - user3857017
1
让我感到不安的是教授们只教授做什么,惩罚你不这样做,却不解释为什么。using关键字可能会引起很多问题;我尽量不使用它(这意味着在大多数情况下代码非常冗长)。 - Qix - MONICA WAS MISTREATED
显示剩余2条评论
4个回答

3
这是那种你可以喝啤酒讨论数小时,却仍然没有一个每个人都满意的答案的问题。如果您几乎总是使用 std:: 的功能,则在文件开头添加 using namespace std; 并不是一个坏主意。另一方面,如果您使用来自多个命名空间的内容(例如,使用 llvm:: 编写编译器并同时使用 std::),则可能会混淆哪些部分属于 llvm,哪些部分属于std - 因此在我的编译器项目中,我没有单独一个文件使用using namespace ...;-而是根据需要编写llvm::std::。有几个函数(也许是不明智的)被称为Type(),有些地方使用something->Type()->Type()来获取我需要的类型-是的,有时候甚至让我感到困惑... 我还有很多像Constants :: ConstDeclToken :: RightParen这样的东西,以便我可以快速看到“这是什么”。所有这些都可以更简短和“简单”,但我更喜欢看到大部分时间所属的位置。更冗长有助于更容易地看到事物所属的位置-但这需要更多的打字和阅读,因此需要平衡。

1
我没有一个单独的文件使用命名空间...; +1; 我知道许多开发人员(包括我自己)更喜欢这种方式。 - Qix - MONICA WAS MISTREATED
2
@Qix,只是为了明确,当我快速编写一些代码来展示“如何完成”(例如在这里),我可能会使用using namespace std;。但对于我的编译器项目和其他较大的项目,我不会这样做。 - Mats Petersson
当然,那是明智的。 - Qix - MONICA WAS MISTREATED

1
一般为什么?
命名是软件开发中更难的方面之一。初学者往往不知道他们的名称选择可能会在以后产生歧义。
特别是,我们的软件行话通常对某些问题有首选术语。这些偏好可能导致无关的类实例使用相同(或相似)符号和类似的含义。
我经常使用的一些符号包括init(),exec(),load(),store(),我在许多地方使用timeStampGet()。我还使用open(),close(),send()/ recv()或write()/ read()。
因此,我可以在每个3个命名空间中重命名init()和5个对象,但指定我想要的一个更简单。
我在2个命名空间和12个对象中找到了exec()。我使用3种不同的timeStampGet()方法。无论是命名空间、函数还是类方法,这些符号对我来说都有意义。
此外,我发现5个字符的“std::”命名空间前缀非常自然,并且比全局的“using namespace std”更可取。我想这需要练习。

还有一种情况是当命名空间或类名过长时,我会添加一个typedef短名称...以下是一些来自生产代码的示例:

typedef ALARM_HISTORY   ALM_HST;
typedef MONITOR_ITEM    MI
typedef BACKUP_CONTROL  BC;

在一个团队中,我们同意使用明确定义的“完整”名称,但有时因其长度而感到疲劳。 项目后期,我们同意可以使用typedef(用于简短的类或命名空间名称),如果它们简单且不会引起混淆。

这有点超出我的理解范围,但至少我认为我对我的问题的主要内容有所了解,谢谢! - user3857017

1

个人而言,我不喜欢“using”声明。在我看来,它们使代码难以阅读并破坏了命名空间。我作为一名维护程序员已经工作了20年,我讨厌任何使代码更难以阅读的东西-在我看来,“using”和throw规范一样无用。

更易读的是什么?在6个月、1年或10年后?

UDP::Socket sock
sock.send(data)
TCP::Socket sock2
sock2.send(data)

vs

using UDP;
using TCP;
sock.send(data)
sock2.end(data)

我也不喜欢命名空间别名

using namespace po = boost::program_options; 

现在你增加了一个间接层,使得下一个程序员需要查找po与boost::program_options的区别,从而增加了他们的工作量。同样的情况也适用于那些可怕的typedef。
typedef long QUADWORD;

四双字是多大?长4字节有多长?也许是8字节?在我的操作系统上可能是17字节。

我的观点是,如果您不能打字,那么就不要成为程序员 - 保存的按键不等于可维护的好代码。


1
我认为通常情况下,您不需要在全局声明使用std。如果您制作的是简单应用程序,则可能足够。但是,在大型组织中工作时,通常会使用不同的命名空间,这些命名空间可能具有重叠的对象。如果您在std中拥有一个函数,并在您创建的命名空间中拥有一个函数,然后调用"using namespace std"和"using namespace yournamespace",则在调用该函数时会得到不需要的结果。当您在每个调用前加上命名空间前缀时,它可以保持更清晰,并且不会产生重叠问题。

1
如果您仍然希望 ADL 起作用,那么在每次调用时加前缀的命名空间并不是可行的方法。假设有以下代码:using namespace std; template <typename T> void Foo(T &a, T &b) { swap(a, b); },你该如何改变对 swap 的调用方式以继续允许 namespace N { struct S { }; void swap(S &, S &); },其中 Foo<N::S> 应该调用 N::swap 而不是 std::swap - user743382
2
@user3857017 你可能看到的是类成员访问,它使用与命名空间成员访问相同的语法。给定 A::B::CA 可以是命名空间或类,如果它是一个命名空间,B 可以是命名空间或类。仅从语法上无法确定,您必须查找名称。是的,在某种程度上,这样的类可以用作一种名称空间,但并不完全相同。 - user743382
1
@user3857017 我听过的最好的命名空间定义是它是函数和对象的逻辑分组。因此,std:: 命名空间包含标准对象和函数,例如 stl 容器、std::fill_n()、std::max()、std::sort() 等等。同样,boost:: 命名空间包含 boost 库中的所有内容,而 boost::asio::ip::tcp:: 是处理 boost::asio:: 库中 TCP 连接的对象和函数。 - dgnuff
1
以一个具体的例子来说明,我所工作的服务器有一个包含操作系统特定例程的“System”命名空间。System::Sleep(int microseconds)接受一个以微秒为单位的时间并休眠相应的时间。通过将其放置在命名空间中,我们能够避免与Win32中存在的以毫秒为单位的Sleep()例程发生冲突。因此,在我们的代码中从不使用Sleep()::Sleep(),因为它在Linus下无法使用GCC编译,而只使用System:Sleep() - dgnuff
1
这几乎是正确的。我也不认为它是不负责任的。就像你可能有类函数:Square::Draw()Circle::Draw()Triangle::Draw() 每个函数都做了不同的事情,在你的例子中,各种 foo() 例程可以做不同的事情。关键是知道何时使用 C++ 的这个特性。如果使用得当,它就是完全合理的。boost::asio::ip::tcp::socket::send()boost::asio::ip::udp::socket::send() 就是一个例子,其中有两个 send() 方法,仅由它们所在的命名空间区分。 - dgnuff
显示剩余12条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接