问题描述
刚开始拼贴,我在编程领域是新手。因此,当我学习C ++时,遇到了一个让我不敢问的问题:“为什么我必须在代码中包括“使用命名空间std”,才能在已经拥有iostream的情况下进行读写操作?” ,因为有人告诉我iostream库中已经定义了“ cin / cout”,但是我注意到,如果我仅编写这些行之一,将会产生编译错误。而且,因此,iostream和“ std”命名空间之间是什么关系...请有人解释一下吗?谢谢!
解决方法
#include与使用
简单来说:
#include <iostream> // include the header
int main() {
// now you can use stuff declared in that header
std::cout << "Hello world" << std::endl;
// if you are lazy you can "use" things:
using std::cout;
using std::endl;
cout << "Hello World" << endl;
}
您不必不必写using namespace std;
!经常这样做的情况很少见,而实际上确实造成重大伤害的情况如此频繁,以至于您可以记住:永远不要使用它!有关详细信息,请参见此处:Why is “using namespace std;” considered bad practice?。重要的是要认识到,完全限定名称std::cout
与不完全限定名称cout
之间的区别不只是要或多或少地输入5个字符(请阅读...)。
库与名称空间
库和名称空间之间有什么关系?
标准库将所有内容放入std
命名空间中。命名空间有助于使事物分开。一个不同的库可以包含一个other_namespace::vector
,并且不会与std::vector
混淆,因为我们有名称空间。
很棒的东西
使用名称空间的更深层原因是Argument Dependent Lookup。我将尝试用一个简单的例子来解释。假设您正在使用带有某些功能模板的库,该模板可以对必须提供的类型的对象进行某些处理:
namespace library {
template<typename T>
void do_something(T& a,T& b){
std::cout << "wrong...\n";
std::swap(a,b); // (1)
std::cout << "correct\n";
using std::swap;
swap(a,b); // (2)
}
}
我拿了两个对象并将它们交换两次。您必须忍受我第二秒钟才能理解为什么(1)是错误的,而只有(2)是正确的。现在,我们有了一个库函数模板,要使用它,我们需要某种类型的T
:
namespace A {
struct foo{};
void swap(foo& a,foo& b) {
std::cout << "A::swap" << "\n";
}
}
想象一下foo
这样的实例,比std::swap
到swap
实例更好。实际上foo
是空的,因此对于swap
两个对象,我们什么都不要做。
回顾一下:标准库随附std::swap
。有人编写了一个我们想使用的库(称为library
)。我们希望库代码调用A::swap
而不是std::swap
。库作者甚至都不知道A::swap
存在。
连同上面的A
和library
,此代码
int main() {
A::foo a,b;
library::do_something(a,b);
}
将打印:
wrong...
correct
A::swap
Live Example。发生了什么?这行:
std::swap(a,b); // (1)
毫无疑问,会打std::swap
。不是我们想要的。我们希望库代码调用我们的A::swap
。
现在,这个:
using std::swap;
swap(a,b); // (2)
第一行将名称swap
从std
拉到函数范围内。在第二行中,ADL最终加入,因为它说swap
而不是std::swap
。简而言之,ADL是:a
和b
来自命名空间A
,因此,当编译器搜索所有可能的swap
时,它也会在A
中搜索。如果它在A
中找到一个,则调用它(如果在A
中找不到一个,则仍然有swap
来自std
)。因此,只有(2)个调用我们的自定义交换。
这只能与名称空间一起使用。 “很酷的东西”是库作者不需要了解有关您的名称空间的任何信息,但是他们的库代码仍然会从您的名称空间调用您的函数(如果存在)。
我应该注意,并非所有代码都是通用库代码。通常,您想编写代码来知道每个细节中发生了什么,您想知道调用了哪些函数。通常,您不希望代码的行为取决于是否包含特定的标头。因此,使用完全限定的函数调用std::foo
可以使很多代码变得更好。
结论
我希望我可以说服您,名称空间不仅仅是或多或少地输入一些字符。 using namespace std;
的懒惰完全错过了名称空间的要点。另一方面,通过using std::foo; foo();
将名称拉入范围是
完全正常并启用ADL。
库和名称空间是按照约定关联的。
按照惯例,库提供给程序员用户的符号包含在名称空间中。这样可以组织事物,并且有一些高级语言功能(ADL)意味着名称空间中的代码的行为与外部代码的行为不同。
当您键入using namespace std;
时,您告诉编译器“当遇到符号时,还应查看std
以查找是否可以确定它是什么”。在“文件”范围内执行此操作通常是一个非常糟糕的主意;在单个short函数中执行此操作是可用的,但超出此范围则可能导致真正棘手的错误。
与namespace std
进行交互的标准,专业方法是在符号前面加上名称空间:
std::cout << "Hello world\n";
而不是
using namespace std;
cout << "Hello world\n";
绝对不会:
using namespace std;
int main() {
cout << "Hello world\n";
}
您还可以抓取单个符号,这不如导入整个名称空间那么糟:
using std::cout;
cout << "Hello world\n";
但也应避免在“文件”范围内使用。
#include <iostream>
这包括系统搜索路径中名为iostream
的头文件。 iostream
是标准库的一部分。按照惯例(和C ++标准),iostream
为您的程序提供的符号位于namespace std
中。
通过将符号放在命名空间中,可以避免与您的代码发生冲突。 std
中有很多符号,并且如果#include <iostream>
将一些未知数量的符号塞入全局命名空间中,您很容易得到错误或以意外方式调用的错误函数。
std::cout
和using namespace std; cout
和using std::cout
都是告诉编译器在哪个命名空间中找到符号cout
的方法。
#include <iostream>
在cout
中包含namespace std
;没有它,您的代码将不会意识到它的存在。
C ++,C具有文本包含模型。 #include
实际上获取文件iostream
的内容,并将其复制/粘贴到您的文件中。然后,您的编译器将读取扩展文件,并在<iostream>
中找到符号。
因为这种文字包含可能会导致很多 东西,所以将它们隔离到namespace
可以为您(程序员)避免麻烦。
最近,C ++添加了模块。模块是#include
指令的替代方法,因为它可以直接从库中获取符号,然后将其注入到您的代码中,而无需粘贴大量的。
在模块中,名称空间仍未直接连接到模块。你可以
import std;
或
import std.iostream;
,这将仅将std
中的namespace std
库符号导入到您的代码中。 (C ++标准添加了模块,但尚未对std库进行模块化,因此上述名称仅供参考)。
符号查找未直接连接到符号导入。
这使符号导入可以大块完成,而查找则更仔细。
, iostream是一个库。它是某人为您编写的代码,因此您不必这样做。通过添加#include <iostream>
,您可以告诉预处理器粘贴该代码。但是此代码提供的功能和结构的名称可能会干扰其他名称。但这不是问题,因为您可以通过将它们放在命名空间中来分离它们,STL(上游的一部分)使用std
(标准,发音的缩写)来实现。作为“站起来”)。当名称空间中有内容时,您必须命名该名称空间才能访问其中的内容。即std::cout
。但是有时候,您不想每次都想从STL访问内容时都不必写std::
。这就是using namespace std
为您所做的。这样,您只需键入cout
。但这是very bad idea!
,
图书馆
库中有部分代码预先编写,可为您提供功能。可以采用函数/重载运算符等形式。
有两种类型的库:
-
标准库
#include <iostream>
,库的名称放在尖括号中。 -
用户定义/制造的例如
#include "randomLib.h"
,并且库名用双引号引起来。
命名空间
当您的项目需要多个库时。两者都有可能包含多个具有相同名称的方法(函数定义),或者单个库可能使用相同的函数名称,但名称空间不同。命名空间可以消除编译器和用户的困惑或歧义。
- 让我们说lib 1具有
namespace abc{ foo(); }
,而lib 2具有namespace def{ foo(); }
因此,您将为所需的功能执行abc::foo()
或def::foo()
。这里abc / def是namespace
,::
是作用域解析运算符,foo()
是您正在调用的方法。