其他人告诉我,using namespace std;
用代码编写是错误的,我应该直接使用std::cout
and std::cin
。
为什么被using namespace std;
认为是不好的做法?它是效率低下还是有可能声明模棱两可的变量(与std
命名空间中的函数具有相同名称的变量)?它会影响性能吗?
其他人告诉我,using namespace std;
用代码编写是错误的,我应该直接使用std::cout
and std::cin
。
为什么被using namespace std;
认为是不好的做法?它是效率低下还是有可能声明模棱两可的变量(与std
命名空间中的函数具有相同名称的变量)?它会影响性能吗?
这根本与性能无关。但是考虑一下:您正在使用两个名为 Foo 和 Bar 的库:
using namespace foo;
using namespace bar;
一切正常,您可以毫无问题地Blah()
从 Foo 和Bar 调用。Quux()
但是有一天你升级到 Foo 2.0 的新版本,它现在提供了一个名为Quux()
. 现在你遇到了一个冲突: Foo 2.0 和 Bar 都导入Quux()
到你的全局命名空间中。这将需要一些努力来解决,特别是如果函数参数恰好匹配。
如果您使用了foo::Blah()
and bar::Quux()
,那么引入 offoo::Quux()
将是一个非事件。
我同意Greg 写的一切,但我想补充一点:它甚至可能比 Greg 说的更糟!
Library Foo 2.0 可以引入一个函数,与多年来调用的代码相比,它对于您Quux()
的某些调用而言无疑是更好的匹配。然后你的代码仍然可以编译,但它默默地调用了错误的函数并做了上帝知道的事情。这几乎是最糟糕的事情了。Quux()
bar::Quux()
请记住,std
命名空间有大量标识符,其中许多是非常常见的标识符(想想list
, sort
, string
,iterator
等),它们也很可能出现在其他代码中。
如果您认为这不太可能:在我给出这个答案大约半年后,Stack Overflow 上出现了一个问题,几乎完全发生了这种情况(由于省略前缀而调用了错误的函数)。这是此类问题的另一个更新示例。所以这是一个真正的问题。std::
这里还有一个数据点:很多很多年前,我也曾经发现必须在标准库中的所有内容前面加上std::
. 然后我在一个项目中工作,一开始就决定using
禁止指令和声明,除了函数范围。你猜怎么着?我们大多数人花了几个星期来习惯编写前缀,再过几个星期,我们大多数人甚至同意它实际上使代码更具可读性。这是有原因的:您喜欢较短还是较长的散文是主观的,但前缀客观地增加了代码的清晰度。不仅是编译器,您也可以更轻松地查看引用了哪个标识符。
十年后,该项目发展到拥有数百万行代码。由于这些讨论一次又一次地出现,我曾经很好奇(允许的)函数范围using
在项目中实际使用的频率。我搜索了它的来源,只找到了一两打使用它的地方。对我来说,这表明,一旦尝试,std::
即使在允许使用的地方,即使每 100 kLoC 使用一次 using 指令,开发人员也不会感到痛苦。
底线:明确地为所有内容添加前缀不会造成任何伤害,几乎不需要习惯,并且具有客观优势。特别是,它使编译器和人类读者更容易解释代码——这可能是编写代码时的主要目标。
放入using namespace
类的头文件的问题在于,它迫使任何想要使用您的类(通过包括您的头文件)的人也“使用”(即查看所有其他名称空间)。
但是,您可以随意在您的(私有)*.cpp 文件中添加 using 语句。
请注意,有些人不同意我这样说“随意”——因为尽管using
cpp 文件中的语句比头文件中的语句更好(因为它不会影响包含您的头文件的人),但他们认为仍然不是好(因为根据代码的不同,它可能会使类的实现更难维护)。这个 C++ Super-FAQ 条目说,
using 指令适用于遗留 C++ 代码并简化向命名空间的转换,但您可能不应该定期使用它,至少不要在新的 C++ 代码中使用它。
常见问题解答提出了两种选择:
使用声明:
using std::cout; // a using-declaration lets you use cout without qualification
cout << "Values:";
只需输入 std::
std::cout << "Values:";
我最近遇到了有关Visual Studio 2010的投诉。事实证明,几乎所有的源文件都有这两行:
using namespace std;
using namespace boost;
很多Boost功能正在进入 C++0x 标准,而 Visual Studio 2010 有很多 C++0x 功能,所以突然这些程序无法编译。
因此,避免using namespace X;
是一种面向未来的形式,一种确保对正在使用的库和/或头文件的更改不会破坏程序的方法。
短版:不要using
在头文件中使用全局声明或指令。随意在实现文件中使用它们。以下是Herb Sutter和Andrei Alexandrescu在C++ 编码标准中对这个问题的看法(粗体字是我的重点):
概括
命名空间使用是为了您的方便,而不是让您强加于他人:切勿在 #include 指令之前编写 using 声明或 using 指令。
推论:在头文件中,不要写命名空间级别的 using 指令或 using 声明;相反,明确命名空间限定所有名称。(第二条规则从第一条开始,因为标头永远无法知道其他标头#includes 可能出现在它们之后。)
讨论
简而言之:您可以并且应该在#include 指令之后在您的实现文件中自由地使用声明和指令的命名空间,并且对此感觉良好。尽管反复断言相反,使用声明和指令的命名空间并不是邪恶的,它们不会破坏命名空间的目的。相反,它们使命名空间变得可用。
不应using
在全局范围内使用该指令,尤其是在标头中。但是,在某些情况下,即使在头文件中也是合适的:
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
...) 要好,因为它更短并且能够处理用户定义的浮点类型(通过参数相关查找(ADL))。
只有在全局使用时才被认为是“坏的” 。因为:
using namespace xyz;
。using namespace std;
您可能不知道您抓取的所有内容 - 当您添加另一个#include
或移动到新的 C++ 修订版时,您可能会遇到您不知道的名称冲突。继续并在本地(几乎)自由使用它。当然,这可以防止你重复std::
——而且重复也是不好的。
在C++03中有一个成语——样板代码——用于为你的类实现一个swap
函数。建议您实际使用本地using namespace std;
- 或至少using 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(...)`
}
这具有以下魔力:
std::swap
for value_
,即void std::swap(int, int)
。void swap(Child&, Child&)
实现了重载,编译器将选择它。void std::swap(Child&,Child&)
并尽力交换这些。对于C++11,没有理由再使用这种模式了。的实现std::swap
已更改以找到潜在的过载并选择它。
另一个原因是惊喜。
如果我看到cout << blah
,而不是std::cout << blah
我想:这是什么cout
?这是正常的cout
吗?有什么特别的吗?
有经验的程序员使用任何可以解决他们的问题的方法并避免任何产生新问题的方法,并且出于这个确切原因,他们会避免使用头文件级别的使用指令。
有经验的程序员也尽量避免在其源文件中完全限定名称。造成这种情况的一个次要原因是,除非有充分的理由,否则在更少的代码就足够的情况下编写更多的代码并不优雅。造成这种情况的一个主要原因是关闭参数相关查找 (ADL)。
这些好的理由是什么?有时程序员明确想要关闭 ADL,有时他们想要消除歧义。
所以以下是可以的:
我同意它不应该在全球范围内使用,但在本地使用并不是那么邪恶,比如在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
}
在此示例中,我们解决了由其组成引起的潜在名称冲突和歧义。
在那里显式声明的名称(包括通过 using 声明声明的名称,如His_lib::String
)优先于通过 using 指令(using namespace Her_lib
)在另一个范围内可访问的名称。
我也认为这是一种不好的做法。为什么?就在一天,我认为命名空间的功能是划分东西,所以我不应该把所有东西都扔进一个全局包中来破坏它。
但是,如果我经常使用 'cout' 和 'cin',我using std::cout; using std::cin;
会在 .cpp 文件中写#include
:我认为没有理智的人会命名流cout
或cin
. ;)
很高兴看到代码并知道它的作用。如果我看到std::cout
我知道那是图书馆的cout
流。std
如果我看到cout
那我不知道。它可能是图书馆的cout
流。std
或者在同一个函数中可能有int cout = 0;
十行以上。或该文件中static
命名的变量。cout
它可以是任何东西。
现在拿一百万行代码库,这不是特别大,并且您正在寻找一个错误,这意味着您知道这百万行中有一行没有完成它应该做的事情。cout << 1;
可以读取一个static int
named cout
,将其向左移动一位,然后丢弃结果。寻找错误,我必须检查。你能看到我真的很喜欢看std::cout
吗?
如果您是一名教师,并且从来不需要编写和维护任何代码为生,那么这似乎是一个非常好的主意。我喜欢在哪里看到代码(1)我知道它做了什么;并且,(2)我相信写它的人知道它的作用。
这一切都与管理复杂性有关。使用命名空间会拉入你不想要的东西,因此可能会使调试变得更加困难(我说可能)。到处使用 std:: 更难阅读(更多文本等等)。
课程用马 - 以您最好的方式和感觉能力管理您的复杂性。
考虑
// myHeader.h
#include <sstream>
using namespace std;
// someoneElses.cpp/h
#include "myHeader.h"
class stringstream { // Uh oh
};
请注意,这是一个简单的示例。如果您有包含 20 个包含和其他导入的文件,那么您将需要通过大量依赖项来解决问题。更糟糕的是,根据冲突的定义,您可能会在其他模块中出现不相关的错误。
这并不可怕,但您可以通过不在头文件或全局命名空间中使用它来避免头疼。在非常有限的范围内这样做可能没问题,但我从来没有遇到过输入额外的五个字符来阐明我的函数来自哪里的问题。
一个具体的例子来澄清这个问题。想象一下,你有两个库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
。这是不可取的,但幸运的是它很容易(只需在编译器标记为模棱两可foo::
的所有调用之前添加)。a
但是想象一下另一种情况,其中 bar 改为如下所示:
namespace bar {
void a(int) { /* Does something completely different */ }
}
此时,您的调用a(42)
突然绑定到bar::a
而foo::a
不是做“某事”而不是“做某事”,而是做“完全不同的事情”。没有编译器警告或任何东西。您的程序只是默默地开始做一些与以前完全不同的事情。
当您使用命名空间时,您会面临这样的情况,这就是人们不喜欢使用命名空间的原因。命名空间中的东西越多,冲突的风险就越大,因此人们可能std
比使用其他命名空间更不舒服(由于该命名空间中的东西的数量)。
最终,这是可写性与可靠性/可维护性之间的权衡。可读性也可能是一个因素,但我可以看到这两种方式的论点。通常我会说可靠性和可维护性更重要,但在这种情况下,您将不断为相当罕见的可靠性/可维护性影响支付可写成本。“最佳”权衡将决定您的项目和优先事项。
您需要能够阅读与您有不同风格和最佳实践意见的人编写的代码。
如果您只使用cout
,没有人会感到困惑。但是,当您有很多名称空间飞来飞去并且您看到这个类并且您不确定它的作用时,让名称空间显式充当某种注释。乍一看,“哦,这是一个文件系统操作”或“那是在做网络工作”。
同时使用许多命名空间显然是灾难的根源,但在我看来,使用 JUST 命名空间std
和仅命名空间std
并不是什么大不了的事,因为重新定义只能通过您自己的代码进行......
因此,只需将它们视为保留名称,如“int”或“class”,就是这样。
人们应该停止对它如此肛门。你的老师一直都是对的。只需使用一个命名空间;这就是首先使用命名空间的重点。您不应该同时使用多个。除非是你自己的。所以再一次,重新定义不会发生。
命名空间是一个命名范围。命名空间用于对相关声明进行分组,并将单独的项目分开。例如,两个单独开发的库可能使用相同的名称来引用不同的项目,但用户仍然可以同时使用两者:
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++ 编程语言概述
我同意这里的其他人的观点,但我想解决关于可读性的问题——你可以通过简单地在文件、函数或类声明的顶部使用 typedefs 来避免所有这些。
我通常在我的类声明中使用它,因为类中的方法倾向于处理相似的数据类型(成员),并且 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 namespace std
,这也是算法库中的函数。
#include <iostream>
#include <algorithm>
using namespace std;
int count = 1;
int main() {
cout << count << endl;
}
它不会使您的软件或项目性能变差。在源代码开头包含命名空间还不错。包含的using namespace std
说明会根据您的需求以及您开发软件或项目的方式而有所不同。
namespace std
包含 C++ 标准函数和变量。当您经常使用 C++ 标准函数时,此命名空间很有用。
如本页所述:
使用命名空间 std 的语句通常被认为是不好的做法。此语句的替代方法是在每次声明类型时使用作用域运算符 (::) 指定标识符所属的命名空间。
并看到这个意见:
当您大量使用命名空间并确定不会发生任何冲突时,在源文件中使用“使用命名空间 std”没有问题。
有人说using namespace std
在源文件中包含 是一种不好的做法,因为您正在从该名称空间调用所有函数和变量。当您想定义一个与包含在其中的另一个函数同名的新函数时,namespace std
您将重载该函数,并且可能由于编译或执行而产生问题。它不会像您期望的那样编译或执行。
如本页所述:
尽管该语句使我们免于键入 std:: 时我们希望访问定义在 std 命名空间中的类或类型,但它会将整个 std 命名空间导入程序的当前命名空间。让我们举几个例子来理解为什么这可能不是一件好事
...
现在在开发的后期阶段,我们希望使用另一个版本的 cout,它是在某个名为“foo”的库中自定义实现的(例如)
...
请注意如何存在歧义,cout 指向哪个库?编译器可能会检测到这一点而不编译程序。在最坏的情况下,程序可能仍然编译但调用了错误的函数,因为我们从未指定标识符属于哪个命名空间。
这是逐案的。我们希望将软件在其生命周期内的“总拥有成本”降至最低。声明“使用命名空间标准”有一些成本,但不使用它也有易读性成本。
人们正确地指出,在使用它时,当标准库引入新的符号和定义时,您的代码将停止编译,您可能会被迫重命名变量。然而,从长远来看,这可能是好的,因为如果您将关键字用于某些令人惊讶的目的,未来的维护者会暂时感到困惑或分心。
你不希望有一个名为vector的模板,比如说,它不是其他人都知道的vector。并且因此在 C++ 库中引入的新定义的数量足够少,可能根本不会出现。必须进行这种更改是有成本的,但成本并不高,并且被不将std
符号名称用于其他目的而获得的清晰度所抵消。
考虑到类、变量和函数的数量,std::
对每一个进行说明可能会使您的代码混乱 50%,并使您更难理解。可以在一屏代码上采用的算法或方法中的步骤现在需要来回滚动才能遵循。这是一个真正的成本。可以说,这可能不是一个高成本,但否认它甚至存在的人是缺乏经验、教条主义或完全错误的。
我会提供以下规则:
std
与所有其他图书馆不同。它是每个人基本上都需要知道的一个库,在我看来,最好将其视为语言的一部分。一般来说,using namespace std
即使没有其他库,也有一个很好的案例。
切勿通过将其放在标头中来将决定强加给编译单元(.cpp 文件)的作者using
。始终将决定权交给编译单元作者。即使在一个决定在using namespace std
任何地方都使用的项目中,也可能会罚款一些最好作为该规则的例外处理的模块。
尽管命名空间功能允许您拥有许多具有相同符号定义的模块,但这样做会让人感到困惑。尽可能保持名称不同。即使不使用命名空间功能,如果您有一个名为的类foo
并std
引入了一个名为 的类foo
,那么从长远来看,重命名您的类可能会更好。
使用命名空间的另一种方法是通过为命名空间符号添加前缀来手动添加它们。我有两个已经使用了几十年的库,实际上都是从 C 库开始的,其中每个符号都以“AK”或“SCWin”为前缀。一般来说,这就像避免使用“使用”结构,但你不写双冒号。AK::foo()
而是AKFoo()
. 它使代码更密集 5-10% 并且更少冗长,唯一的缺点是如果您必须使用两个具有相同前缀的此类库,您将遇到大麻烦。请注意,X Window 库在这方面非常出色,只是它们忘记了使用一些#defines:TRUE 和 FALSE 应该是 XTRUE 和 XFALSE,这与同样使用 TRUE 和 FALSE 的 Sybase 或 Oracle 建立了命名空间冲突具有不同的价值观!(在数据库的情况下是 ASCII 0 和 1!)这样做的一个特殊优点是它无缝地应用于预处理器定义,而 C++ using
/namespace
系统不处理它们。这样做的一个很好的好处是,它提供了从成为项目的一部分到最终成为图书馆的有机坡度。在我的一个大型应用程序中,所有窗口类都带有前缀Win
, 所有信号处理模块 Mod 等等。这些中的任何一个都几乎没有被重用的机会,因此将每个组都变成一个库并没有实际的好处,但是在几秒钟内,项目如何分解为子项目就很明显了。
我同意其他人的观点——它要求名称冲突、含糊不清,而事实是它不那么明确。虽然我可以看到 的用途using
,但我个人的偏好是限制它。我也会强烈考虑其他人指出的内容:
如果您想找到一个可能是相当常见的名称的函数名称,但您只想在std
命名空间中找到它(或相反——您想更改所有不在命名空间std
、命名空间X
...中的调用),那你打算怎么做呢?
您可以编写一个程序来完成它,但是花时间在您的项目本身上工作而不是编写一个程序来维护您的项目不是更好吗?
就个人而言,我实际上并不介意std::
前缀。我喜欢它而不是没有它。我不知道这是否是因为它是明确的并且对我说“这不是我的代码......我正在使用标准库”或者它是否是其他东西,但我认为它看起来更好。考虑到我最近才开始接触 C++(使用并且仍然使用 C 和其他语言的时间更长,而且 C 是我一直以来最喜欢的语言,就在汇编之上),这可能很奇怪。
还有另一件事,尽管它与上述内容和其他人指出的内容有些相关。虽然这可能是不好的做法,但我有时会保留std::name
标准库版本和特定程序实现的名称。是的,确实这可能会咬你,咬你,但这一切都归结为我从头开始这个项目,我是它唯一的程序员。示例:我重载std::string
并调用它string
。我有有用的补充。我这样做的部分原因是我的 C 和 Unix (+ Linux) 倾向于小写名称。
除此之外,您可以拥有命名空间别名。这是一个可能没有被提及的有用的示例。我使用 C++11 标准,特别是 libstdc++。好吧,它没有完全的std::regex
支持。当然,它可以编译,但它会抛出一个异常,因为它是程序员端的错误。但它缺乏实施。
所以这就是我解决它的方法。安装 Boost 的正则表达式,并将其链接。然后,我执行以下操作,以便当 libstdc++ 完全实现它时,我只需要删除此块并且代码保持不变:
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;
}
我不会争论这是否是一个坏主意。然而,我会争辩说,它为我的项目保持清洁,同时使其具体:确实,我必须使用 Boost,但我使用它就像 libstdc++ 最终会拥有它一样。是的,开始您自己的项目并从一开始就使用标准(...)对于帮助维护、开发和与项目相关的一切有很大帮助!
只是为了澄清一些事情:我实际上并不认为在STL中故意使用类的名称/任何东西来代替是一个好主意。字符串对我来说是个例外(忽略第一个、上面或第二个,如果必须的话,双关语),因为我不喜欢“字符串”的想法。
事实上,我仍然非常偏向 C 和偏向 C++。保留细节,我工作的大部分内容更适合 C不那么封闭,不那么傲慢,更容易接受。)。但是有用的是一些人已经提出的建议:我确实使用 list (它是相当通用的,不是吗?),并且排序(同样的事情)来命名两个如果我这样做会导致名称冲突using namespace std;
,所以为此,我更喜欢具体、可控并且知道如果我打算将其作为标准用途,那么我将不得不指定它。简单地说:不允许假设。
至于使 Boost 的正则表达式成为std
. 我这样做是为了将来的整合,而且——再次,我完全承认这是偏见——我不认为它像boost::regex:: ...
. 的确,这对我来说是另一回事。C++ 中有很多东西在外观和方法上我还没有完全接受(另一个例子:可变参数模板与 var 参数 [尽管我承认可变参数模板非常有用!])。即使是那些我确实接受的人也很困难,我仍然对他们有意见。
根据我的经验,如果您有多个使用 say, 的库cout
,但出于不同的目的,您可能会使用错误的cout
.
例如,如果我输入using namespace std;
andusing namespace otherlib;
并且输入 just cout
(碰巧在两者中),而不是std::cout
(or 'otherlib::cout'
),您可能会使用错误的,并得到错误。使用起来更加有效和高效std::cout
。
我不认为在所有情况下都一定是不好的做法,但是在使用它时需要小心。如果您正在编写一个库,您可能应该将范围解析运算符与命名空间一起使用,以防止您的库与其他库发生冲突。对于应用程序级代码,我看不出有什么问题。
"Why is 'using namespace std;' considered a bad practice in C++?"
I put it the other way around: Why is typing five extra characters considered cumbersome by some?
Consider e.g. 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?
对于不合格的导入标识符,您需要像grep这样的外部搜索工具来找出声明标识符的位置。这使得推理程序正确性变得更加困难。
这是一种不好的做法,通常称为全局命名空间污染。当多个命名空间具有相同的函数名称和签名时,可能会出现问题,那么编译器将无法确定调用哪个命名空间,而当您使用函数调用指定命名空间时,这一切都可以避免,例如std::cout
. 希望这可以帮助。:)
这取决于它的位置。如果它是一个公共标头,那么您通过将其合并到全局名称空间中来减少名称空间的价值。请记住,这可能是使模块全局变量的一种巧妙方法。
为了回答你的问题,我实际上是这样看待的:很多程序员(不是全部)调用命名空间 std。因此,人们应该养成不使用与命名空间 std 中的内容冲突或使用相同名称的东西的习惯。这是理所当然的,但与严格来说可以提出的可能连贯的单词和假名的数量相比,这并不算多。
我的意思是真的......说“不要依赖这个存在”只是让你依赖它不存在。您会经常遇到借用代码片段并不断修复它们的问题。只需将您的用户定义和借用的东西保持在应有的有限范围内,并且非常谨慎地使用全局变量(老实说,全局变量几乎总是为了“立即编译,稍后理智”的最后手段)。真的,我认为这是您老师的坏建议,因为使用 std 将适用于“cout”和“std::cout”,但不使用 std 仅适用于“std::cout”。您不会总是有幸编写自己的所有代码。
注意:在您真正了解编译器的工作原理之前,不要过多地关注效率问题。有了一点编码经验,您不必对它们了解太多,然后您就会意识到它们能够将好的代码概括为简单的东西。每一点都像你用 C 写整个东西一样简单。好的代码只有它需要的复杂程度。
老实说,对我来说,这就像讨论缩进的空格数。
在标头中使用指令会导致损坏。但是在 C++ 文件中呢?也许如果您一次使用两个命名空间。但如果你使用一个,它更多的是关于风格而不是真正的效率。
你知道为什么关于缩进的话题如此受欢迎吗?任何人都可以谈论它,听起来非常聪明和经验丰富。
有一个非常简单的答案:它是防御性编程。您知道 , 等的使用std::size_t
可以std::cout
更容易一些using namespace std;
- 我希望您不需要相信这样的指令在标题中没有位置!但是,在翻译单元中,您可能会受到诱惑……
作为命名空间一部分的类型、类等std
随着每个 C++ 版本的增加而增加。std::
如果您放宽限定词,则会有太多潜在的歧义。放宽您将经常使用的名称的限定词是完全合理的,例如,或者更可能是:- 但除非这些已经是语言中易于理解的部分(或者特别是 C 的包装库),只需使用限定符。std
using std::fprintf;
using std::size_t;
std
当您可以使用typedef
、结合auto
和decltype
推理时,从可读性/可维护性的角度来看,确实没有什么可收获的。
#include <iostream>
using namespace std;
int main() {
// There used to be
// int left, right;
// But not anymore
if (left != right)
std::cout << "Excuse me, WHAT?!\n";
}
所以为什么?因为它引入了与常用变量名重叠的标识符,并让这段代码编译,将其解释为if (std::left != std::right)
.
PVS-Studio可以使用 V1058 诊断找到这样的错误:https ://godbolt.org/z/YZTwhp (谢谢 Andrey Karpov !!)。
ping cppcheck 开发人员:您可能希望标记这个。这真是太棒了。
是的,命名空间很重要。在我的项目中,我需要将一个 var 声明导入到我的源代码中,但是在编译它时,它与另一个第三方库发生冲突。
最后,我不得不通过其他方式解决它,并使代码变得不那么清晰。
我认为在本地或全球使用应该取决于应用程序。
因为,当我们在本地使用库时,有时代码会变得一团糟。可读性会降低。
因此,我们应该仅在可能发生冲突时才在本地使用库。
我不是一个更有经验的人。所以,如果我错了,请告诉我。
命名空间是为了避免命名冲突。C++ 基于 C 并且 C 在函数和变量名称方面存在许多问题,因为有时来自不同库的函数会发生冲突。所以库开发者开始在他们的函数前面加上库名,如下所示:
foo/foo.h
:
void libfoo_foo_foo_h_open(); // the name can be weird then even this one!
C++ 引入了命名空间来以一种简单的方式解决这个问题。
假设您有两个名为file
并window
分别处理文件和窗口的库以及以下代码:
#include <file.h>
#include <window.h>
using namespace file;
using namespace window;
void open() {
...
}
file.h
:
namespace file {
void open(); // What!
}
window.h
:
namespace window {
void open(); // Oh no!
}
上面的代码肯定会编译失败。
如果您不喜欢类型std::
(只有 5 个字符),您可以随时这样做:(在头文件中不是一个好主意)
using s = std;
如果你仍然想using namespace std;
在你的源文件中使用,那么你就是在邀请这个问题,我不得不问你“命名空间的目的是什么? ”。
这是一个示例,显示如何using namespace std;
导致名称冲突问题:
在示例中,一个非常通用的算法名称 ( std::count
) 名称与一个非常合理的变量名称 ( ) 冲突count
。
这是我在其他任何答案中都没有找到的观点:仅使用一个命名空间。根据大多数答案,命名空间不好的主要原因是您可能有冲突的函数名称,这可能会导致一团糟。但是,如果您只使用一个命名空间,则不会发生这种情况。确定您将使用最多的库(也许是using namespace std;
)并坚持使用它。
可以将其视为具有不可见的库前缀 -std::vector
变为 just vector
。在我看来,这是两全其美的:一方面,它减少了你必须做的输入量(正如命名空间所期望的那样),另一方面,它仍然需要你使用前缀来清晰和安全。如果有一个没有命名空间前缀的函数或对象 - 你知道它来自你声明的一个命名空间。
请记住,如果您决定在全球范围内使用一个 - 不要在本地使用其他。这又回到了其他答案,即本地命名空间通常比全局命名空间更有用,因为它们提供了多种便利。
为什么使用命名空间标准?
C++ 有一个标准库,其中包含您在构建应用程序时使用的通用功能,例如容器、算法等。如果这些使用的名称是公开的,例如,如果它们在全局范围内定义了一个队列类,那么您将永远无法再次使用相同的名称而不会发生冲突。所以他们创建了一个命名空间,std 来包含这个变化。
不使用的原因 1:不良做法
使用命名空间 std 的语句通常被认为是不好的做法。该语句的替代方法是在每次声明类型时使用作用域运算符 (::) 指定标识符所属的命名空间。尽管该语句使我们免于在我们希望访问定义在 std 命名空间中的类或类型时键入 std:: ,但它会将整个 std 命名空间导入程序的当前命名空间。
不使用的原因 2:编译器感到困惑
在玩具程序中导入整个 std 库是可以的,但在生产级代码中,这很糟糕。使用命名空间标准;使命名空间 std 中声明的每个符号都可以在没有命名空间限定符的情况下访问。
例如:
现在,假设您升级到更新版本的 C++,并且更多新的 std 命名空间符号被注入到您的程序中,而您对此一无所知。您可能已经在程序中使用了这些符号。现在编译器将很难确定声明的符号是属于您自己的实现还是来自您在不知道的情况下导入的命名空间。一些编译器会抛出错误。如果你不走运,编译器选择了错误的实现并编译它,这肯定会导致运行时崩溃。
命名空间污染效果:
虽然这种做法对于示例代码是可以的,但将整个 std 命名空间拉入全局命名空间并不好,因为它违背了命名空间的目的并可能导致名称冲突。这种情况称为命名空间污染。
只要您不在头文件中使用'using namespace std',它应该是完全正常的。
如果与 say boost 存在命名冲突,那么您不要在特定的 .cpp 文件中使用此语句,但仍然可以通过不在其他文件中的每一行重复 'std::' 十次来节省大家的眼睛和手指。
不幸的是,现在这是一个纯粹的宗教问题,每个人都更容易/更有效率地遵循一遍又一遍地重复“std::”的建立模式。