我听说过使用名称空间std;这是不好的做法,我应该直接使用std::cout和std::cin。这是为什么?声明与std命名空间中的某个变量同名的变量是否有风险?


当前回答

我同意其他人的看法——这是在要求名字冲突、歧义,但事实是它不够明确。虽然我可以看到使用的用途,但我个人倾向于限制它。我也会强烈考虑其他人所指出的:

如果你想找到一个可能是一个相当常见的名称的函数名,但你只想在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(但这是一个很好的练习,也是让自己学会另一种语言的好方法)。不要对对象/类等抱有偏见,这可能更好地表述为不那么封闭、不那么傲慢、更容易接受。)。但有用的是一些人已经提出的建议:我确实使用了列表(它相当通用,不是吗?),并排序(相同的事情)以命名两个,如果我使用名称空间std;,因此,为了达到这个目的,我更喜欢具体、可控,并且知道如果我打算将其作为标准用途,那么我必须指定它。简单地说:不允许假设。

至于让Boost的正则表达式成为标准的一部分。我这样做是为了将来的集成,而且——再次,我完全承认这是偏见——我不认为它像Boost::正则表达式::。。。。事实上,这对我来说是另一回事。C++中有很多东西我还没有完全接受,包括外观和方法(另一个例子:varadic模板与var参数[尽管我承认varadic模版非常有用!])。即使那些我确实接受的人也很困难,我仍然对他们有意见。

其他回答

我同意这里的其他观点,但我想解决有关可读性的问题——您可以通过在文件、函数或类声明的顶部使用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;
}

or:

// .cpp
std::vector<std::string> File::ReadLines()
{
    std::vector<std::string> lines;
    // Get them...
    return lines;
}

同时使用多个命名空间显然是一个灾难,但在我看来,使用JUST命名空间std和仅使用命名空间std并不是什么大问题,因为重新定义只能由您自己的代码进行。。。

因此,只需将它们的函数视为保留名称,如“int”或“class”即可。

人们不应该对此如此吹毛求疵。你的老师一直都是对的。只需使用一个名称空间;这就是首先使用名称空间的全部意义。您不应该同时使用多个。除非它是你自己的。因此,再次定义不会发生。

这是个案。我们希望在软件的生命周期内最大限度地降低软件的“总拥有成本”。声明“using namespace std”有一定的代价,但不使用它也有易读性的代价。

人们正确地指出,当使用它时,当标准库引入新的符号和定义时,您的代码将停止编译,您可能被迫重命名变量。然而,这可能是一个很好的长期目标,因为如果您出于某种令人惊讶的目的使用关键字,未来的维护人员将暂时感到困惑或分心。

你不希望有一个名为vector的模板,比如说,它不是其他人都知道的向量。因此,C++库中引入的新定义的数量非常少,可能根本找不到。必须进行这种更改是有代价的,但代价并不高,而且通过不将std符号名称用于其他目的而获得的清晰性来抵消。

考虑到类、变量和函数的数量,在每一个上声明std::可能会使代码出错50%,并使您更难理解。一个算法或方法中的一个步骤可以在一屏代码上执行,现在需要前后滚动才能执行。这是一个真实的成本。可以说,这可能不是一个高成本,但那些否认它存在的人是缺乏经验的、教条的,或者根本就是错误的。

我提供以下规则:

std不同于所有其他库。这是每个人基本上都需要知道的一个库,在我看来,最好将其视为语言的一部分。一般来说,使用命名空间std是一个很好的例子,即使没有其他库。永远不要通过将此using放在头中来迫使编译单元(.cpp文件)的作者做出决定。始终将决定权交给编译单元的作者。即使在一个决定在任何地方使用名称空间std的项目中,也可能会有一些模块作为该规则的例外处理。尽管名称空间功能允许您有许多定义相同符号的模块,但这样做会令人困惑。请尽可能保持名称不同。即使不使用名称空间特性,如果您有一个名为foo的类,并且std引入了一个名foo的类型,那么长期来说重命名您的类可能会更好。使用名称空间的另一种方法是通过前缀手动命名符号。我有两个库,我用了几十年,实际上都是从C库开始的,每个符号的前缀都是“AK”或“SCWin”。一般来说,这就像避免使用“using”构造,但不使用双冒号。AK::foo()改为AKFoo()。它使代码更密集5-10%,更不冗长,唯一的缺点是,如果必须使用两个具有相同前缀的这样的库,则会遇到很大的麻烦。注意,X Window库在这方面非常出色,只是它们忘记了使用一些#定义:TRUE和FALSE应该是XTRUE和XFALSE,这与Sybase或Oracle产生了命名空间冲突,它们同样使用了TRUE和FALSE,但值不同!(在数据库的情况下为ASCII 0和1!)这样做的一个特殊优点是,它似乎适用于预处理器定义,而C++使用/命名空间系统不处理它们。这样做的一个好处是,它提供了从项目的一部分到最终成为图书馆的有机坡度。在我的一个大型应用程序中,所有的窗口类都以Win为前缀,所有的信号处理模块都以Mod为前缀等等。这些模块中的任何一个都不太可能被重用,因此将每个组放入一个库中没有实际的好处,但几秒钟后就可以看出项目是如何分解为子项目的。

考虑两个名为Foo和Bar的库:

using namespace foo;
using namespace bar;

一切都很好,你可以从Foo调用Blah(),从Bar调用Qux()。但有一天你升级到了Foo 2.0的新版本,它现在提供了一个名为Qux()的函数。现在出现了一个冲突:Foo 2.0和Bar都将Qux()导入到全局命名空间中。这将需要一些努力来解决,特别是如果函数参数恰好匹配。

如果您使用了foo::Blah()和bar::Qux(),那么引入foo::Qux()将是一个非事件。

我不认为这在任何情况下都是不好的做法,但在使用它时都需要小心。如果你正在编写一个库,你可能应该在命名空间中使用作用域解析运算符,以防止你的库与其他库冲突。对于应用程序级代码,我看不出它有什么问题。