提问人:akbiggs 提问时间:9/21/2009 最后编辑:Mateen Ulhaqakbiggs 更新时间:7/13/2023 访问量:1196107
为什么“使用命名空间标准”被认为是不好的做法?
Why is "using namespace std;" considered bad practice?
问:
我听说这是不好的做法,我应该直接使用。
为什么会这样?声明与命名空间中的内容同名的变量是否有风险?using namespace std;
std::cout
std::cin
std
答:
另一个原因是惊喜。
如果我看到,而不是我想:这是什么?这是正常的吗?有什么特别的吗?cout << blah
std::cout << blah
cout
cout
评论
cout
future
std::future
考虑两个名为 Foo 和 Bar 的库:
using namespace foo;
using namespace bar;
一切正常,您可以从 Foo 和 Bar 毫无问题地拨打电话。但是有一天,您升级到 Foo 2.0 的新版本,它现在提供了一个名为 .现在你遇到了一个冲突:Foo 2.0 和 Bar 都导入到你的全局命名空间中。这将需要一些努力来修复,特别是如果函数参数恰好匹配。Blah()
Quux()
Quux()
Quux()
如果您使用了 和 ,那么引入 将不是一个事件。foo::Blah()
bar::Quux()
foo::Quux()
评论
error
list
sort
这一切都与管理复杂性有关。使用命名空间会拉入你不想要的东西,因此可能会使调试变得更加困难(我说可能)。在任何地方使用 std:: 更难阅读(更多文本等等)。
课程的马匹 - 以最佳方式管理您的复杂性,并感觉自己有能力。
评论
这取决于它的位置。如果它是一个公共标头,则通过将命名空间合并到全局命名空间中来减少命名空间的值。请记住,这可能是制作模块全局变量的一种巧妙方法。
考虑
// myHeader.h
#include <sstream>
using namespace std;
// someoneElses.cpp/h
#include "myHeader.h"
class stringstream { // Uh oh
};
请注意,这是一个简单的示例。如果您有包含 20 个包含和其他导入的文件,您将需要通过大量依赖项来找出问题所在。更糟糕的是,您可能会在其他模块中获得不相关的错误,具体取决于冲突的定义。
这并不可怕,但通过不在头文件或全局命名空间中使用它,您可以避免头痛。在非常有限的范围内执行此操作可能是可以的,但是我从未遇到过键入额外的五个字符以阐明我的函数来自哪里的问题。
评论
using namespace std
放入类的头文件的问题在于,它迫使任何想要使用您的类(通过包含您的头文件)的人也“使用”(即查看其他命名空间中的所有内容)。using namespace
但是,您可以随意将 using 语句放在您的(私有)*.cpp 文件中。
请注意,有些人不同意我这样说的“随意”——因为尽管 cpp 文件中的语句比头文件中的语句更好(因为它不会影响包含头文件的人),但他们认为它仍然不好(因为根据代码的不同,它可能会使类的实现更难维护)。这个 C++ 超级常见问题解答条目说,using
using-directive 存在于旧版 C++ 代码中,以简化向命名空间的转换,但您可能不应该定期使用它,至少不应该在新的 C++ 代码中使用它。
常见问题解答建议了两种替代方案:
使用声明:
using std::cout; // a using-declaration lets you use cout without qualification cout << "Values:";
只需输入 std::
std::cout << "Values:";
评论
如果导入正确的头文件,则全局范围中突然出现十六进制
、左
、加号
或计数
等名称。如果您不知道包含这些名称,这可能会令人惊讶。如果您还尝试在本地使用这些名称,可能会导致相当多的混淆。std::
如果所有标准内容都在其自己的命名空间中,则不必担心与代码或其他库的名称冲突。
评论
distance
<iomanip>
<iomanip>
<iostream>
<iomanip>
setw
我不认为这在所有条件下都一定是不好的做法,但使用时需要小心。如果你正在编写一个库,你可能应该将作用域解析运算符与命名空间一起使用,以防止你的库与其他库发生冲突。对于应用程序级代码,我看不出它有什么问题。
你需要能够阅读那些与你有不同风格和最佳实践意见的人编写的代码。
如果您只使用 ,没有人会感到困惑。但是,当你有很多命名空间飞来飞去,你看到这个类,但你并不完全确定它的作用时,让命名空间显式充当某种注释。乍一看,你可以看到,“哦,这是一个文件系统操作”或“正在做网络工作”。
cout
它可能比格雷格写的更糟糕!
库 Foo 2.0 可以引入一个函数,该函数与多年来调用的代码相比,它显然更适合您的某些调用。然后你的代码仍然可以编译,但它会静默地调用错误的函数,并且天知道会发生什么。这和事情可能变得一样糟糕。Quux()
Quux()
bar::Quux()
请记住,命名空间有大量的标识符,其中许多是非常常见的标识符(例如、、、等),它们也很可能出现在其他代码中。std
list
sort
string
iterator
如果您认为这不太可能:在我给出这个答案大约半年后,在 Stack Overflow 上提出了一个问题,几乎完全发生了这个问题(由于省略了前缀而调用了错误的函数)。这是另一个最近的例子。
所以这是一个真正的问题。std::
这里还有一个数据点:很多很多年前,我也曾经发现必须用 .然后我参与了一个项目,在一开始就决定禁止指令和声明,除了函数范围。你猜怎么着?我们大多数人花了几周的时间来习惯编写前缀,几周后,我们大多数人甚至同意它实际上使代码更具可读性。这是有原因的:你喜欢短篇还是长篇散文是主观的,但前缀客观地增加了代码的清晰度。不仅是编译器,您也更容易看到所引用的标识符。std::
using
在十年内,这个项目发展到有几百万行代码。由于这些讨论一次又一次地出现,我曾经很好奇(允许的)函数范围在项目中实际使用的频率是多少。我搜索了它的来源,只找到了一二十个使用它的地方。在我看来,这表明,一旦尝试过,开发人员也不会发现使用使用指令的痛苦程度,即使每 100 kLoC 使用一次,即使允许使用。using
std::
一句话:在所有内容前面明确添加前缀不会造成任何伤害,几乎不需要习惯,并且具有客观优势。特别是,它使代码更容易被编译器和人类读者解释——这可能是编写代码时的主要目标。
评论
foo
Foo
Foo::Bar
Foo::bar
foo::bar
我也认为这是一种不好的做法。为什么?就在有一天,我以为命名空间的功能是划分东西,所以我不应该把所有东西都扔进一个全局包里来破坏它。
但是,如果我经常使用“cout”和“cin”,我会在 .cpp 文件中写:(从不在头文件中,因为它传播 )。我认为没有一个理智的人会命名一个流或.;)using std::cout; using std::cin;
#include
cout
cin
评论
不应在全局范围内使用该指令,尤其是在标头中。但是,在某些情况下,即使在头文件中也是如此: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));
}
这比显式限定 (, ...) 更好,因为它更短,并且能够使用用户定义的浮点类型(通过依赖于参数的查找 (ADL))。std::sin
std::cos
评论
using std::cos;
using std::sin
userlib
sin
cos
using namespace userlib
using namespace std
swap
std::swap
template<typename T> void swap(MyContainer<T>&, MyContainer<T>&)
x
namespace userlib
cos(x)
using namespace userlib;
using namespace std;
using std::sin; using std::cos; using std::exp;
std::*
我最近遇到了一个关于Visual Studio 2010的投诉。事实证明,几乎所有的源文件都有这两行:
using namespace std;
using namespace boost;
许多 Boost 功能都进入了 C++0x 标准,而 Visual Studio 2010 有很多 C++0x 功能,所以突然之间这些程序没有编译了。
因此,避免是一种面向未来的形式,一种确保对正在使用的库和/或头文件的更改不会破坏程序的方法。using namespace X;
评论
using
using namespace
有经验的程序员使用任何能解决他们问题的东西,避免任何产生新问题的东西,并且出于这个确切的原因,他们避免使用头文件级别的 using-directives。
有经验的程序员也会尽量避免在源文件中对名称进行完全限定。造成这种情况的一个小原因是,除非有充分的理由,否则当更少的代码就足够时,编写更多的代码是不优雅的。造成这种情况的一个主要原因是关闭了依赖于参数的查找 (ADL)。
这些很好的理由是什么?有时程序员明确想要关闭 ADL,有时他们希望消除歧义。
所以以下几点是可以的:
- 函数实现中的函数级 using-directives 和 using-declarations
- 源文件内的源文件级 using-declarations
- (有时)源文件级 using-directives
不要全局使用它
只有在全球范围内使用时,它才被认为是“坏的”。因为:
- 你把你正在编程的命名空间弄得乱七八糟。
- 当您使用多个 .
using namespace xyz;
- 对于源代码的其他读者来说,无论对什么是正确的,对于最频繁的读者来说都更是如此:你自己。一两年后再来看看......
- 如果你只谈论,你可能不知道你抓取的所有东西 - 当你添加另一个或移动到一个新的 C++ 修订版时,你可能会得到你不知道的名称冲突。
using namespace std;
#include
您可以在本地使用它
继续在本地(几乎)自由使用它。当然,这阻止了你重复 -- 重复也是不好的。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(...)`
}
这有以下魔力:
- 编译器将选择 for ,即 .
std::swap
value_
void std::swap(int, int)
- 如果实现了重载,编译器将选择它。
void swap(Child&, Child&)
- 如果你没有这个重载,编译器将使用并尽力交换它们。
void std::swap(Child&,Child&)
在 C++11 中,没有理由再使用此模式。更改了实现,以查找潜在的重载并选择它。std::swap
评论
swap
std::swap
std::swap
using std::swap;
using namespace std;
swap
swap
using std::swap
using namespace std
std::swap
swap
std::swap
std::swap
using std::swap
是的,命名空间很重要。进入我的项目后,我需要将一个 var 声明导入到我的源代码中,但在编译它时,它与另一个第三方库冲突。
最后,我不得不通过其他方式解决它,并使代码不那么清晰。
对于不合格的导入标识符,您需要像 grep 这样的外部搜索工具来找出标识符的声明位置。这使得对程序正确性的推理变得更加困难。
“为什么'使用命名空间标准'在C++中被认为是一种不好的做法?”
我反过来说:为什么输入五个额外的字符被一些人认为很麻烦?
例如,考虑编写一个数值软件。当“vector”是问题域最重要的概念之一时,为什么我甚至会考虑通过将一般的“std::vector”削减为“vector”来污染我的全局命名空间?
评论
cout << hex << setw(4) << i << endl;
std::cout << std::hex << std::setw(4) << i << std::endl;
std::map<std::string,std::pair<std::string,std::string>>
map<string,pair<string,string>>
为了回答你的问题,我实际上是这样看的:很多程序员(不是全部)调用命名空间 std。因此,人们应该养成不使用与命名空间 std 中的内容相同的内容或使用相同名称的东西的习惯。这是一笔不小的数额,但与严格来说可以想出的可能连贯的单词和假名的数量相比,还算不上什么。
我的意思是真的......说“不要依赖它的存在”只是让你依赖它不存在。您经常会遇到借用代码片段并不断修复它们的问题。只需将用户定义和借用的东西保持在有限的范围内,并且非常谨慎地使用全局变量(老实说,出于“现在编译,以后健全”的目的,全局变量几乎总是最后的手段)。真的,我认为这是你老师的坏建议,因为使用 std 对“cout”和“std::cout”都有效,但不使用 std 只对“std::cout”有效。你不会总是足够幸运地编写你自己的所有代码。
注意:在你真正了解编译器的工作原理之前,不要过分关注效率问题。有了一点编码经验,你不必对它们了解太多,你就会意识到它们能够将好的代码概括成简单的东西。每一点都像你用 C 语言编写整个代码一样简单,好的代码只在它需要的程度上复杂。
评论
<algorithm>
count
distance
log
destroy
launch
visit
beta
sample
messages
clamp
erase
copy
modulus
left
std
我同意它不应该在全球范围内使用,但在本地使用并没有那么邪恶,就像在.下面是“C++ 编程语言”中的一个示例:namespace
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 声明声明的名称,如 )优先于通过 using 指令 () 在另一个作用域中可访问的名称。His_lib::String
using namespace Her_lib
评论
{..}
我认为本地或全局使用应该取决于应用程序。
因为,当我们在本地使用库时,有时代码会一团糟。可读性会变低。
因此,只有当可能发生冲突时,我们才应该在本地使用库。
我不是一个更有经验的人。所以,如果我错了,请告诉我。
同时使用多个命名空间显然是灾难的根源,但在我看来,使用 JUST 命名空间和仅命名空间没什么大不了的,因为重新定义只能通过您自己的代码进行......std
std
因此,只需将它们视为保留名称,例如“int”或“class”,仅此而已。
人们应该停止对它如此肛门。你的老师一直都是对的。只需使用一个命名空间;这就是首先使用命名空间的全部意义所在。您不应同时使用多个。除非是你自己的。所以再说一次,重新定义不会发生。
评论
min
end
less
std::
std::
很高兴看到代码并知道它的作用。如果我看到,我知道那是图书馆的流。如果我看到,那我不知道。它可能是库的流。或者在同一函数中可能有 10 行。或者该文件中命名的变量。它可以是任何东西。std::cout
cout
std
cout
cout
std
int cout = 0;
static
cout
现在拿一百万行代码库来说,这并不是特别大,你正在寻找一个错误,这意味着你知道在这一百万行中有一行没有做它应该做的事情。 可以读取一个命名,将其向左移动一位,然后扔掉结果。寻找错误,我必须检查一下。你能看到我真正喜欢看到的样子吗?cout << 1;
static int
cout
std::cout
如果您是一名教师并且从未以编写和维护任何代码为生,这似乎是一个非常好的主意。我喜欢看到代码:(1)我知道它的作用;(2)我相信写它的人知道它的作用。
评论
我同意其他人的观点——它要求名称冲突、模棱两可,然后事实是它不那么明确。虽然我可以看到 的用途,但我个人的偏好是限制它。我也会强烈考虑其他人指出的内容:using
如果你想找到一个函数名称,它可能是一个相当常见的名称,但你只想在命名空间中找到它(或者相反——你想改变所有不在命名空间、命名空间、...)中的调用,那么你打算如何做到这一点?std
std
X
你可以写一个程序来做这件事,但是花时间在你的项目本身上,而不是写一个程序来维护你的项目不是更好吗?
就个人而言,我实际上并不介意前缀。我喜欢这种外观,而不是没有它。我不知道这是否是因为它很明确,对我说“这不是我的代码......我正在使用标准库“,或者如果它是其他东西,但我认为它看起来更好。这可能很奇怪,因为我最近才开始接触 C++(使用并且仍然使用 C 和其他语言的时间要长得多,而 C 是我一直以来最喜欢的语言,就在汇编之上)。std::
还有一件事,尽管它与上述内容以及其他人指出的内容有些相关。虽然这可能是不好的做法,但我有时会保留标准库版本和名称,以便特定于程序的实现。是的,这确实会咬你,狠狠地咬你,但归根结底,我从头开始了这个项目,我是唯一的程序员。示例:我重载并调用它。我有有用的补充。我这样做的部分原因是我的 C 和 Unix (+ Linux) 倾向于使用小写名称。std::name
std::string
string
除此之外,您还可以拥有命名空间别名。下面是一个示例,说明它可能没有被提及的有用之处。我使用 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(但这是一个很好的练习,也是让自己 a. 学习另一种语言和 b. 尽量不要对对象/类/等产生偏见的好方法,这可能更好地说成不那么封闭,不那么傲慢,更能接受。但有用的是一些人已经建议的:我确实使用列表(它相当通用,不是吗?),并排序(同样的事情)来命名两个,如果我这样做会导致名称冲突,因此为此,我更喜欢具体,控制并知道如果我打算将其作为标准用法,那么我将不得不指定它。简单地说:不允许假设。using namespace std;
至于使 Boost 的正则表达式成为 .我这样做是为了未来的整合,而且——我再次完全承认这是偏见——我不认为它像.事实上,这对我来说是另一回事。在 C++ 中,在外观和方法上,我仍然没有完全接受很多东西(另一个例子:可变模板与 var 参数 [尽管我承认可变模板非常有用!])。即使是那些我接受的人也很困难,我仍然对他们有疑问。std
boost::regex:: ...
评论
std
命名空间是未定义的行为,因此永远不应该这样做。
短版本:不要在头文件中使用全局声明或指令。随意在实现文件中使用它们。以下是Herb Sutter和Andrei Alexandrescu在C++编码标准中对这个问题的看法(粗体强调是我的):using
总结
命名空间 usings 是为了方便您,而不是为了强加给别人:永远不要在 #include 指令之前写 using 声明或 using 指令。
推论:在头文件中,不要编写命名空间级别的 using 指令或 using 声明;相反,显式命名空间限定所有名称。(第二条规则遵循第一条规则,因为标头永远无法知道它们后面可能出现的其他标头 #includes。
讨论
简而言之:在 #include 指令之后,你可以而且应该在实现文件中自由地使用使用声明和指令的命名空间,并且对此感觉良好。尽管一再提出相反的断言,但使用声明和指令的命名空间并不是邪恶的,它们不会破坏命名空间的目的。相反,它们是使命名空间可用的原因。
评论
using
using namespace xyz;
xyz
std
using std::vector;
using namespace
goto
using namespace
using namespace Foo
using namespace Bar
baz(xyz)
Bar::baz()
Foo::baz()
#include
Bar::baz()
using namespace std;
#include <iostream>
cout
#include <iostream>
std::cout
using namespace std;
#include <iosfwd>
一个例子,由于计数的模糊性而抛出编译错误,这也是算法库中的一个函数。using namespace std
#include <iostream>
#include <algorithm>
using namespace std;
int count = 1;
int main() {
cout << count << endl;
}
评论
::count
--问题解决了。通常,来自 std 命名空间的内容会比其他地方的内容更多,因此保留 using namespace 指令可能会节省您键入的内容。
我同意这里的其他人,但我想解决有关可读性的问题 - 您可以通过简单地在文件、函数或类声明的顶部使用 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;
}
评论
命名空间是一个命名范围。命名空间用于对相关声明进行分组并保持独立 项目分开。例如,两个单独开发的库可能使用相同的名称来指代不同的 项,但用户仍可同时使用:
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++编程语言概述
评论
以下示例显示了如何导致名称冲突问题:using namespace std;
在此示例中,一个非常通用的算法名称 () 名称与一个非常合理的变量名称 () 冲突。std::count
count
根据我的经验,如果您有多个库使用 比如,,但出于不同的目的,您可能会使用错误的 .cout
cout
例如,如果我输入 and 和 just 键入(恰好在两者中),而不是 (或 ),您可能会使用错误的,并出现错误。使用起来更有效和高效。using namespace std;
using namespace otherlib;
cout
std::cout
'otherlib::cout'
std::cout
一个具体的例子来澄清这个问题。想象一下,你有两个库,每个库都有自己的命名空间:foo
bar
namespace foo {
void a(float) { /* Does something */ }
}
namespace bar {
...
}
现在,假设您在自己的程序中使用 and,如下所示: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
归根结底,这是可写性与可靠性/可维护性之间的权衡。可读性也可能是因素,但我可以看到任何一种方式的论点。通常我会说可靠性和可维护性更重要,但在这种情况下,您将不断为相当罕见的可靠性/可维护性影响支付可写性成本。“最佳”权衡将决定您的项目和优先级。
评论
using
using
这是我在任何其他答案中都没有找到的观点:只使用一个命名空间。根据大多数答案,命名空间不好的主要原因是,函数名称可能会发生冲突,这可能会导致一团糟。但是,如果仅使用一个命名空间,则不会发生这种情况。确定您将使用最多(也许)的库并坚持使用。using namespace std;
人们可以把它想象成一个不可见的库前缀 - 变得只是.在我看来,这是两全其美的:一方面,它减少了你必须做的输入量(正如命名空间所期望的那样),另一方面,它仍然需要你使用前缀来确保清晰和安全性。如果有一个没有命名空间前缀的函数或对象 - 你知道它来自你声明的一个命名空间。std::vector
vector
请记住,如果您决定在全球范围内使用一个 - 不要在本地使用其他。这又回到了其他答案,即本地命名空间通常比全局命名空间更有用,因为它们提供了多种便利。
老实说,对我来说,这就像讨论缩进的空格数量。
在标头中使用指令会造成损坏。但是在 C++ 文件中呢?也许如果你同时使用两个命名空间。但如果你使用一个,它更多的是关于风格而不是真正的效率。
你知道为什么关于缩进的线程如此受欢迎吗?任何人都可以对此发表意见,听起来非常聪明和有经验。
评论
if (left != right)
using std::namespace;
left
right
std
.cpp
这是一种不好的做法,通常称为全局命名空间污染。当多个命名空间具有相同的函数名称和签名时,可能会出现问题,然后编译器决定调用哪一个是模棱两可的,当您使用函数调用指定命名空间时,可以避免这种情况,例如 .希望这会有所帮助。:)std::cout
它不会使您的软件或项目性能变差。在源代码的开头包含命名空间还不错。说明的包含范围根据您的需求以及您开发软件或项目的方式而有所不同。using namespace std
包含 C++ 标准函数和变量。当您经常使用 C++ 标准函数时,此命名空间非常有用。namespace std
如本页所述:
使用命名空间 std 的语句通常被认为是错误的 实践。此语句的替代方法是指定 标识符所属的命名空间,使用作用域运算符 (::) 每次我们声明一个类型。
并看到这个意见:
在源文件中使用“using namespace std”没有问题 当您大量使用命名空间并确定 没有什么会碰撞。
有些人说,在源文件中包含 是一种不好的做法,因为你是从该命名空间调用所有函数和变量。当您想要定义一个与中包含的另一个函数同名的新函数时,您将使该函数重载,并且可能会因编译或执行而产生问题。它不会按预期编译或执行。using namespace std
namespace std
如本页所述:
尽管该语句使我们不必键入 std:: 每当 我们希望访问 std 命名空间中定义的类或类型,它 将整个 std 命名空间导入到当前命名空间中 的程序。让我们举几个例子来理解为什么会这样 可能不是一件好事
...
现在在开发的后期阶段,我们希望使用另一个版本的 cout 在某个名为“foo”的库中自定义实现(用于 示例)
...
请注意,cout 指向哪个库存在歧义? 编译器可能会检测到这一点,但不会编译程序。在最坏的情况下 情况下,程序可能仍会编译但调用错误的函数,因为 我们从未指定标识符属于哪个命名空间。
这是个案。我们希望在软件的整个生命周期内将“总拥有成本”降至最低。声明“使用命名空间标准”有一些成本,但不使用它也会在易读性方面产生成本。
人们正确地指出,在使用它时,当标准库引入新的符号和定义时,您的代码会停止编译,您可能会被迫重命名变量。然而,从长远来看,这可能是件好事,因为如果你将一个关键词用于一些令人惊讶的目的,未来的维护者会暂时感到困惑或分心。
比如说,你不希望有一个名为 vector 的模板,它不是其他人都知道的向量。因此,在 C++ 库中引入的新定义数量很少,可能根本无法出现。必须进行这种更改是有成本的,但成本并不高,并且可以通过不将符号名称用于其他目的而获得的清晰度来抵消。std
考虑到类、变量和函数的数量,对每个类、变量和函数进行说明可能会使你的代码增加 50%,并使你更难理解。现在可以在一个代码屏幕上采用的算法或方法中的步骤现在需要来回滚动才能跟随。这是一个真正的成本。可以说,它可能不是一个高昂的成本,但否认它存在的人是缺乏经验的、教条主义的,或者干脆是错误的。std::
我会提供以下规则:
std
与所有其他库不同。它是每个人都需要知道的一个库,在我看来,最好将其视为语言的一部分。一般来说,即使没有其他库,也有一个很好的案例。using namespace std
切勿将此决定强加给编译单元(.cpp文件)的作者,方法是将其放在标题中。始终将决定权交给编译单元作者。即使在一个决定到处使用的项目中,也可能会罚款一些模块,这些模块最好作为该规则的例外处理。
using
using namespace std
尽管命名空间功能允许您拥有许多定义相同符号的模块,但这样做会令人困惑。尽可能保持名称不同。即使不使用命名空间功能,如果您有一个名为的类并引入了一个名为 的类,那么从长远来看,重命名您的类可能更好。
foo
std
foo
使用命名空间的替代方法是通过在符号前面添加前缀来手动设置命名空间符号。我有两个库,我已经使用了几十年,实际上都是从 C 库开始的,其中每个符号都以“AK”或“SCWin”为前缀。一般来说,这就像避免使用“使用”结构,但你不写双冒号。 是 .它使代码的密度提高了 5-10%,并且不那么冗长,唯一的缺点是,如果您必须使用两个具有相同前缀的此类库,您将遇到大麻烦。请注意,X Window 库在这方面非常出色,只是它们忘记了这样做的一些 #defines:TRUE 和 FALSE 应该是 XTRUE 和 XFALSE,这设置了与 Sybase 或 Oracle 的命名空间冲突,后者同样使用具有不同值的 TRUE 和 FALSE!(在数据库的情况下为 ASCII 0 和 1!这样做的一个特殊优点是它完美地应用于预处理器定义,而 C++ / 系统不处理它们。这样做的一个很好的好处是,它提供了一个有机的斜坡,从成为项目的一部分到最终成为一个图书馆。在我的一个大型应用程序中,所有窗口类都带有前缀,所有信号处理模块都是 Mod,等等。这些中的任何一个都不太可能被重用,因此将每个组变成一个库没有实际的好处,但它在几秒钟内就清楚地表明了项目如何分解为子项目。
AK::foo()
AKFoo()
using
namespace
Win
评论
答案很简单:这是防御性编程。您知道 、 等的使用可以变得更容易一些 - 我希望您不需要确信这样的指令在标头中没有位置!然而,在翻译单元中,您可能会受到诱惑......std::size_t
std::cout
using namespace std;
命名空间中的类型、类等会随着每个 C++ 修订而增加。如果放宽限定符,则有太多潜在的歧义。放宽您将经常使用的名称的限定符是完全合理的,例如,,或者更可能的内容: - 但除非这些已经是语言中很好理解的部分(或者具体来说,是 C 库的包装),否则只需使用限定符。std
std::
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 开发人员:您可能希望标记此。这真是昏昏欲睡。
命名空间是为了避免命名冲突。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!
}
上面的代码肯定会编译失败。
如果您不喜欢类型(只有 5 个字符),您可以随时这样做:(在头文件中不是一个好主意)std::
using s = std;
如果你仍然想在你的源文件中使用,那么你就邀请了这个问题,我必须问你“命名空间的目的是什么?using namespace std;
为什么使用命名空间标准?
C++ 有一个标准库,其中包含您在构建应用程序(如容器、算法等)时使用的常见功能。如果这些名称是公开的,例如,如果它们全局定义了一个队列类,则您将永远无法再次使用相同的名称而不会发生冲突。因此,他们创建了一个命名空间 std 来包含此更改。
不使用的原因 1:不良做法
使用 namespace std 的语句通常被认为是不好的做法。此语句的替代方法是在每次声明类型时使用作用域运算符 (::) 指定标识符所属的命名空间。尽管该语句使我们在希望访问 std 命名空间中定义的类或类型时不必键入 std::,但它会将整个 std 命名空间导入到程序的当前命名空间中。
不使用的原因 2:编译器感到困惑
在玩具程序中导入整个 std 库是可以的,但在生产级代码中,这很糟糕。使用命名空间 std;使命名空间 std 中声明的每个符号都可以在没有命名空间限定符的情况下访问。
例如:
现在,假设您升级到较新版本的 C++,并且将更多您不知道的新 std 命名空间符号注入到您的程序中。您可能已经在程序中使用了这些符号。现在,编译器将很难弄清楚声明的符号是属于您自己的实现,还是来自您导入的命名空间,而无需任何想法。某些编译器会引发错误。如果你运气不好,编译器选择了错误的实现并编译它,这肯定会导致运行时崩溃。
命名空间污染影响:
虽然这种做法对于示例代码来说是可以的,但将整个 std 命名空间拉入全局命名空间并不好,因为它违背了命名空间的目的,并可能导致名称冲突。这种情况称为命名空间污染。
评论
只要你不在头文件中使用'using namespace std',它应该是完全正常的。
如果与 boost 存在命名冲突,则不要在特定的 .cpp 文件中使用此语句,但仍然可以通过不在其他文件的每一行上重复“std::” 十次来节省每个人的眼睛和手指。
不幸的是,现在这是一个纯粹的宗教问题,每个人都更容易/更有成效地遵循一遍又一遍地重复“std::”的既定模式。
评论
为什么要避免使用命名空间 std;
?
原因一:为避免名称冲突。
由于 C++ 标准库很大且不断扩展,因此 C++ 中的命名空间用于减少名称冲突。当您使用“using namespace std;”时,您正在批量导入所有内容。
这就是为什么“using namespace std;”永远不会出现在任何专业代码中的原因。
原因二:编译失败。
因为它将 std 命名空间中定义的数百个事物(类、方法、常量、模板等)拉取到全局命名空间中。它这样做,不仅适用于写入“using namespace std”本身的文件,还适用于递归包含它的任何文件。这很容易导致意外的 ODR 违规和难以调试的编译器/链接器错误。
例:
在全局命名空间中声明函数“max”时,请使用 std 命名空间。
由于您没有使用 cmath 标头,因此一切似乎都运行良好。
当其他人包含您的文件和 cmath 标头时,他们的代码意外无法生成,因为全局命名空间中有两个名为“max”的函数。
原因 3:将来可能不起作用。
更糟糕的是,您无法预测将来会对 std:: 命名空间进行哪些更改。这意味着,如果新添加的符号与代码中的某些内容发生冲突,则今天运行的代码以后可能会停止运行。
原因四:维护调试难度大。
使用命名空间 std;可能会产生难以维护和难以调试的代码。这是因为某些方面的来源并不总是很明显。如果开发人员使用术语“string”而不进行更多解释,则他们可能会引用 std::string 类或唯一的字符串类。
命名空间为 std
的代码
#include <iostream>
#include <algorithm>
using namespace std;
int swap = 0;
int main() {
cout << swap << endl; // ERROR: reference to "swap" is ambiguous
}
不带命名空间
#include <iostream>
int main() {
using std::cout; // This only affects the current function
cout << "Hello" <<'\n';
}
但是你可以使用如果,
如果您想制作简短的教程或程序等,您可以使用它。
当您大量使用命名空间并确定不会发生冲突时,在源文件中使用“使用命名空间标准”没有问题。
评论
std::literals::chrono_literals
Poco::Data:Keywords
Poco::Units