作为初学者了解 C++ 中的预处理器指令

Understanding Preprocessor Directives in C++ as a beginner

我最近开始学习C++。作为一名来自 Python 的程序员,我注意到 C++ 中的某些东西如何在 Python 中做同样的事情时有一些普遍的相似之处。

我有一个问题是理解预处理器指令。 I/O Stream 似乎是初学者程序中常用的一种。

#include 与 Python 中的 import 实际上是一回事,还是与导入“模块”完全不同?

C++ 在最新标准 (C++20) 之前没有模块。 #include 与支持模块的语言中的 import 不同。相反,它是一个源代码级包含的“header”文件。通常,headers 仅包含声明而不包含您“导入”的内容的定义。这些定义包含在链接器添加的已编译库中。

恭喜你开始学习 C++,你会遇到更多来自 Python 的问题和困惑,特别是如果你使用一些较新的标准(比如 C++11/14/17 /20).

除此之外,直接回答你的问题:

Is #include effectively the same thing as import in Python or is it completely different than importing "modules."

我不会谈论 C++20 模块,因为各种编译器不完全支持该功能,这不是您的问题。不幸的是,答案不是简单的是或否,而是两者兼而有之。

在 C 和 C++ 中,#include pre-processor 指令本质上是在编译阶段之前对您 #include 的任何文件执行“copy-paste”。这允许您将大块代码分成更易于管理的文件,并且仍然引用所述文件中的代码。

在 Python/C#/Java 和其他各种语言中,您不 #include 想要访问 类 及其功能的文件,您 import 您希望引用的命名空间或模块,JIT 编译器“知道”该模块或命名空间在哪个文件中,从而允许您使用该文件中代码的功能。

Python 和 C++ 不会以相同的方式“构建”代码,因此不会以相同的方式引用部分源代码。

为了更简洁地说明这一点,请看下面的C++代码:

文件:fun.hpp

#define FUN_NUM 1
namespace fun
{
    int get_fun()
    {
        return FUN_NUM;
    }
}

文件:main.cpp

#include <iostream>
#include "fun.hpp"

int main(int argc, char* argvp[])
{
    if (fun::get_fun() == FUN_NUM) {
        std::cout << "Fun!" << std::endl;
    }
    return FUN_NUM;
}

在上面的代码中,当我们#include "fun.hpp"时,C++ pre-processor在编译之前所做的本质上是“copy-and-paste”iostream和[=22中的代码=],因此实际编译的内容类似于以下内容:

文件:main.cpp

// #include <iostream> <- this is replaced with the whole std::iostream file
// not putting that here as it's huge.

// #include "fun.hpp" <- this is replaced with this:
#define FUN_NUM 1
namespace fun
{
    int get_fun()
    {
        return FUN_NUM;
    }
}

int main(int argc, char* argvp[])
{
    if (fun::get_fun() == FUN_NUM) {
        std::cout << "Fun!" << std::endl;
    }
    return FUN_NUM;
}

因为这个“copy-paste”你还需要包含守卫,因为如果你做了类似下面的事情:

文件:main.cpp

#include <iostream>
#include "fun.hpp"
#include "fun.hpp"

int main(int argc, char* argvp[])
{
    if (fun::get_fun() == FUN_NUM) {
        std::cout << "Fun!" << std::endl;
    }
    return FUN_NUM;
}

此代码无法编译,因为您会收到有关重新声明的各种项目的错误,因为编译的内容如下:

文件:main.cpp

// #include <iostream> <- this is replaced with the whole std::iostream file
// not putting that here as it's huge.

// #include "fun.hpp" <- this is replaced with this:
#define FUN_NUM 1
namespace fun
{
    int get_fun()
    {
        return FUN_NUM;
    }
}
// #include "fun.hpp" <- this is replaced with this:
#define FUN_NUM 1
namespace fun
{
    int get_fun()
    {
        return FUN_NUM;
    }
}

int main(int argc, char* argvp[])
{
    if (fun::get_fun() == FUN_NUM) {
        std::cout << "Fun!" << std::endl;
    }
    return FUN_NUM;
}

为了防止双重包含和重新定义,您可以简单地执行以下操作:

文件:fun.hpp

#if !defined(FUN_HPP)
#define FUN_HPP
#define FUN_NUM 1
namespace fun
{
    int get_fun()
    {
        return FUN_NUM;
    }
}
#endif // define FUN_HPP

因此,除非您将 FUN_HPP 作为 pre-processor 定义传递给编译器,否则 FUN_HPP 不会 被定义,直到文件被定义#include 一次,然后在任何其他时候包含它,FUN_HPP 将已经被定义,因此 pre-processor 将不会再次包含代码,从而消除了 double-definitions 的问题。

因此,就您的问题而言,C++ 中的 #include 指令有点像 Python 中的 import 指令,但主要是它们都允许文件正在放入该指令,以便更直接地从 import#include.

访问代码

我希望能增加一点清晰度。