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

问题描述

刚开始拼贴,我在编程领域是新手。因此,当我学习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::swapswap实例更好。实际上foo是空的,因此对于swap两个对象,我们什么都不要做。

回顾一下:标准库随附std::swap。有人编写了一个我们想使用的库(称为library)。我们希望库代码调用A::swap而不是std::swap。库作者甚至都不知道A::swap存在。

连同上面的Alibrary,此代码

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)

第一行将名称swapstd拉到函数范围内。在第二行中,ADL最终加入,因为它说swap而不是std::swap。简而言之,ADL是:ab来自命名空间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::coutusing namespace std; coutusing std::cout都是告诉编译器在哪个命名空间中找到符号cout的方法。

#include <iostream>cout中包含namespace std;没有它,您的代码将不会意识到它的存在。

由C开发的

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! ,

图书馆

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

有两种类型的库:

  1. 标准库#include <iostream>,库的名称放在尖括号中。

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

命名空间

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

  1. 让我们说lib 1具有namespace abc{ foo(); },而lib 2具有namespace def{ foo(); }

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