库和命名空间之间有什么关系?

What's the relation between libraries and namespaces?

提问人:Robert 提问时间:8/28/2020 最后编辑:Robert 更新时间:8/28/2020 访问量:1184

问:

刚开始拼贴,我是编程界的新手。所以,当我学习C++时,我遇到了一个不允许我的问题:“如果我已经有iostream,为什么我需要在我的代码中包含”使用命名空间std“才能编写或读取?”,因为我被告知“cin/cout”已经在iostream库中defind,但我注意到如果我单独编写其中一行,它将给出编译错误。因此,iostream 和“std”命名空间之间有什么关系......有人可以解释一下吗?谢谢!<3

C++ 输入 命名空间 输出 IOSTREAM

评论

3赞 Jesper Juhl 8/28/2020
“为什么我需要包含”使用命名空间 std“ - 你没有
1赞 463035818_is_not_an_ai 8/28/2020
请阅读这篇文章,重要的是你要明白你的前提是错误的:为什么“使用命名空间 std;”被认为是不好的做法?
1赞 Jesper Juhl 8/28/2020
为什么“using namespace std;”被认为是不好的做法?似乎相关..
0赞 rustyx 8/28/2020
通常,命名空间和库是一对一相关的。例如,标准库定义命名空间中的所有内容。std
0赞 Jesper Juhl 8/28/2020
#include <iostream> int main() { std::cout << "Hello world\n"; }-看。不。using namespace ..

答:

3赞 463035818_is_not_an_ai 8/28/2020 #1

#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;
}

不必写!这样做的情况非常罕见,而真正造成巨大伤害的情况非常频繁,根据经验,您可以记住:永远不要使用它!有关详细信息,请参阅此处:为什么“使用命名空间 std;”被认为是不良做法?。必须认识到,完全限定名称和不完全限定名称之间的区别不仅仅是或多或少地键入 5 个字符(请继续阅读......using namespace std;std::coutcout


库与命名空间

库和命名空间之间有什么关系?

标准库将所有内容都放在命名空间中。命名空间有助于将事物分开。不同的库可以包含一个,并且不会混淆,因为我们有命名空间。stdother_namespace::vectorstd::vector


真正酷的东西

使用命名空间的一个更深层次的原因是参数相关查找。我将尝试用一个简单的例子来解释。假设您正在使用一个带有某个函数模板的库,该函数模板对您必须提供的类型的对象执行某些操作:

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";
    }
}

想象是这样的,我们知道比实例更好的方法。实际上是空的,所以对于两个对象,我们什么都不做。foostd::swapswapfooswap

让我们回顾一下:标准库附带了。有人写了一个我们想要使用的库(称为)。我们希望库代码调用而不是 .库作者甚至不知道它的存在。std::swaplibraryA::swapstd::swapA::swap

连同上面的 和 ,这段代码Alibrary

int main() {
    A::foo a,b;
    library::do_something(a,b);
}

将打印:

wrong...
correct
A::swap

现场示例。发生了什么事?这一行:

std::swap(a,b);    // (1) 

电话,毫无疑问。不是我们想要的。我们希望库代码调用我们的 .std::swapA::swap

现在这个:

using std::swap;   
swap(a,b);         // (2)

第一行将名称从函数的作用域中提取出来。在第二行中,ADL 终于开始发挥作用,因为它说没有 .简而言之,ADL 是: 和 来自命名空间 ,因此当编译器搜索所有可能的 s 时,它也会搜索 。如果它找到一个,那么它就会调用它(如果它没有找到一个,那么它仍然来自)。因此,只有 (2) 调用我们的自定义交换。swapstdswapstd::swapabAswapAAAswapstd

这仅适用于命名空间。“很酷的东西”是库作者不需要知道你的命名空间的任何信息,但他们的库代码仍然会从你的命名空间调用你的函数(如果它存在)。

我应该注意,并非所有代码都是通用库代码。通常,您希望在编写代码时知道每个细节中发生了什么,您想知道调用了哪些函数。通常,您不希望代码的行为因是否包含特定标头而有所不同。因此,使用完全限定的函数调用来最好使用大量代码: 。std::foo


结论

我希望我能说服你,命名空间不仅仅是或多或少地键入一些字符。 因为懒惰完全错过了命名空间的重点。另一方面,通过以下方式将名称拉入范围 完全没问题并启用 ADL。using namespace std;using std::foo; foo();

评论

0赞 Robert 8/28/2020
是的,而且......为什么我们需要使用 std:: 如果 cout 和 cin 是在 iostream 中定义的...
2赞 463035818_is_not_an_ai 8/28/2020
@Robert,因为这是他们的全名。为什么我叫你罗伯特而不是伯特?
0赞 Geno C 8/28/2020
有趣的是,你说,这应该在一本介绍性书中介绍,因为在Stroustrup的书《使用C++编程原理和实践》中,他实际上使用了.但是,他确实像您所说的那样提到,要避免对任何命名空间使用指令,但 std 等命名空间除外。但我同意你的看法,你们应该一起避免.只是觉得我应该提一下。using namespace std;using namespace std;
1赞 463035818_is_not_an_ai 8/28/2020
@GenoC 我真的很想知道比亚恩会为他的辩护说些什么。通常的论点是这样的:在简短的例子或空间极其有限的演示幻灯片上是可以的。假设读者知道这仅用于演示,而不是用于实际代码。using namespace std
0赞 Geno C 8/28/2020
@idclev463035818 我同意!
0赞 Nikita Demodov 8/28/2020 #2

IOSTREAM 是一个库。这是有人为你编写的代码,所以你不必这样做。通过添加,您可以告诉预处理器粘贴该代码。但是,此代码提供的函数和结构的名称可能会干扰其他函数和结构。但这不是问题,因为你可以通过将它们放在命名空间中来分隔它们,STL(上游是其中的一部分)使用(标准的缩写,发音为“standod”)来做到这一点。当某个内容位于命名空间中时,您必须命名该命名空间才能访问其中的内容。即 .但有时您不希望每次都想从 STL 访问某些内容时都进行编写。这就是对你的作用。这样,您只需键入 .但这是一个非常糟糕的主意#include <iostream>stdstd::coutstd::using namespace stdcout

评论

1赞 463035818_is_not_an_ai 8/28/2020
fwiw 我认为视频中的那个人错过了真正的问题.他主要是在争论风格和可读性......using namespace std;
0赞 Robert 8/28/2020
为什么 cin 和 cout 会引起混淆,因此需要在命名空间中分隔它们?它们不就是为了一件事而生的吗......写/读?
1赞 463035818_is_not_an_ai 8/28/2020
@Robert假设你编写了自己的 ,因为你把它放在你的命名空间中,然后你有 和 ,如果没有命名空间,你就会有 和 。里面有大量的名称,没有人能记住它们,所以在没有命名空间的情况下,不可能避免冲突coutroberts::coutstd::coutcoutcoutstd
2赞 Yakk - Adam Nevraumont 8/28/2020 #3

库和命名空间按约定相关。

按照惯例,库提供给程序员-用户的符号包含在命名空间中。这样可以组织事物,并且有一些更高级别的语言功能 (ADL) 意味着命名空间中的代码的行为与命名空间外的代码不同。

当你输入时,你告诉编译器“当你遇到一个符号时,也要查看一下,看看你是否能确定它是什么”。在“文件”范围内执行此操作通常是一个非常糟糕的主意;在单个简短的函数中执行此操作是可用的,但超过此范围可能会导致非常棘手的错误。using namespace std;std

标准的、专业的交互方式是用命名空间作为符号的前缀: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>

这包括从系统搜索路径命名的头文件。 是标准库的一部分。按照约定(和 C++ 标准),为程序提供的符号位于 中。iostreamiostreamiostreamnamespace std

通过在命名空间中放置符号,可以避免与代码发生冲突。中有很多很多符号,如果将一些未知数量的符号推入全局命名空间,则很容易出现错误或以意想不到的方式调用错误的函数。std#include <iostream>

std::cout和 和 都是告诉编译器在哪个命名空间中查找符号的方法。using namespace std; coutusing std::coutcout

#include <iostream>包括 ;没有它,你的代码就不知道它的存在。coutnamespace std

C++ 是从 C 发展而来的,C 具有文本包含模型。 实际上获取文件的内容并将其复制/粘贴到您的文件中。然后,编译器读取该扩展文件,并在 中查找符号。#includeiostream<iostream>

因为这种文本包含可能会推很多东西,所以将其隔离到 a 可以防止您(程序员)出现问题。namespace


最近,C++ 添加了模块。模块是指令的替代方法,因为它直接从库中获取符号并将其注入到代码中,而无需大量复制粘贴#include

在模块中,命名空间仍未直接连接到模块。您可以

import std;

import std.iostream;

这只会将仍在 中的库符号导入到您的代码中。(C++标准增加了模块,但还没有模块化std库,所以上面的那些名字都是推测)。stdnamespace std

符号查找不直接连接到符号导入。

这样可以大块地导入符号,同时更仔细地进行查找。

评论

0赞 Robert 8/28/2020
我部分理解这一点,但还有一件事......命名空间是在库中定义的,反之亦然,或者它们是单独定义的,但彼此之间有一些引用?
0赞 Yakk - Adam Nevraumont 8/28/2020
@Robert 按照惯例,它们仅与库相关。按照惯例,编写库的人将代码放入命名空间中。您可以在自己的文件中编写自己的命名空间,也可以在另一个文件中编写另一个命名空间,该命名空间调用两个符号,并且该名称(空)结构。cppnamespace bob { struct hello {}; }namespace alice { struct world {}; }bob::helloalice::world
0赞 vhmvd 8/28/2020 #4

图书馆

库有预先编写的部分代码,以便为您提供功能。可以是函数/重载运算符等的形式。

有两种类型的库:

  1. 标准库,例如 库的名称用尖括号括起来。#include <iostream>

  2. 用户定义/制造,例如 库的名称用双引号括起来。#include "randomLib.h"

命名空间

当您的项目需要多个库时。两者都可能包含多个具有相同名称的方法(函数定义),或者单个库可能使用相同的函数名称,但位于不同的命名空间中。命名空间用于消除编译器和用户的混淆或歧义。

  1. 假设 lib 1 有,lib 2 有namespace abc{ foo(); }namespace def{ foo(); }

因此,您将为您做或为您所需的功能。这里 abc/def 是 ,称为作用域解析运算符,是您要调用的方法。abc::foo()def::foo()namespace::foo()