C++:通过简单的添加示例了解头文件和头保护



我无法将头和标题后卫围绕。我已经阅读了其他问题及其答案,但我仍然无法在Visual Studio 2013中完成这项工作:

main.cpp

#include "stdafx.h"
#include <iostream>
#include "add.h"
int _tmain(int argc, _TCHAR* argv[]) {
    std::cout << "3 + 4 = " << add(3, 4) << std::endl;
    system("pause");
    return 0;
}

add.cpp

#include "stdafx.h" //ADDED LATER; NOW WORKING (AND SEE QUESTION 2 BELOW)
#include "add.h" //ADDED LATER; NOR WORKING (AND SEE QUESTION 2 BELOW)
int add(int x, int y) {
    return x + y;
}

add.h

#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif

当我编译时,控制台窗口在屏幕上闪烁,然后消失。错误列表包含:

错误1错误lnk2019:未解决的外部符号" int __cdecl 添加(int,int)"(?添加@@ yahhh@z)在功能中引用 _wmain C: User Danny Dancy Documents Visual Studio 2013 Projects Addad Program Main Main Main.obj Main

错误2错误LNK1120:1未解决 外部C: Users Danny Documents Visual Studio 2013 projects Adad Program debug main.exe main


1。标题和标头警卫如何工作?我知道如何通过#add.h,它使main.cpp意识到add(int x, int y)的声明,但是如何找到其定义?


2。我的代码中有什么错?

我的代码正在编译。我的代码之所以没有编译的原因是因为我一直在 file> new> file ... 将文件添加到我的项目中,而不是通过 source Files 标题文件 Solution Explorer 的部分在Visual Studio中。我还需要将 #include "stdafx.h添加到add.cpp文件。

以这种方式考虑:预处理每个.cpp文件,然后与其他文件完全分开。

所以让我们首先预处理main.cpp。这涉及查看以#开头的所有行。文件main.cpp仅具有#include行,只需复制其包括的文件内容即可。我将用评论来代表stdafx.hiostream的内容,但实际上我会复制add.h的内容:

// Contents of stdafx.h
// Contents of iostream
#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif
int _tmain(int argc, _TCHAR* argv[]) {
    std::cout << "3 + 4 = " << add(3, 4) << std::endl;
    system("pause");
    return 0;
}

现在看到add.h的内容已将其带入main.cpp中?碰巧的是,这带来了一些更多的预处理指令,因此我们需要做他们说的事情。第一个检查是否尚未定义ADD_H(在此文件中),这不是,因此将所有内容都放在#endif

之前将所有内容留下来。
// Contents of stdafx.h
// Contents of iostream
#define ADD_H
int add(int x, int y);
int _tmain(int argc, _TCHAR* argv[]) {
    std::cout << "3 + 4 = " << add(3, 4) << std::endl;
    system("pause");
    return 0;
}

现在剩余的预处理器指令定义了ADD_H,我们将剩下的最终翻译单元

// Contents of stdafx.h
// Contents of iostream
int add(int x, int y);
int _tmain(int argc, _TCHAR* argv[]) {
    std::cout << "3 + 4 = " << add(3, 4) << std::endl;
    system("pause");
    return 0;
}

现在可以编译此文件。如果您调用add之类的函数,则编译器只需要能够看到该功能的声明即可成功编译。预计该函数将在某些其他翻译单元中定义。

现在,让我们看一下预处理add.cpp。实际上,add.cpp没有任何预处理指令,因此无需发生任何事情。通常,您将#include "add.h",但是如果您不这样做,您的程序仍将编译。因此,在预处理后,我们仍然有:

int add(int x, int y) {
    return x + y;
}

然后进行编译,我们现在有一个add函数的定义。

所有.cpp文件都已编译后,然后将其链接。链接器负责查看编译的main.cpp使用该函数add,因此可以查看其定义。它在编译的add.cpp中找到了定义,并将它们链接在一起。


您可能会想知道为什么我们根本可以包括后卫。在此示例中,它似乎一文不值。没错,在此示例中,它实际上没有任何用途。在那里包括后卫,以防止同一标头被包含在一个文件中。当您拥有更复杂的项目结构时,这很容易发生。但是,让我们看一个不切实际的示例,其中main.cpp包括add.h两次:

#include "stdafx.h"
#include <iostream>
#include "add.h"
#include "add.h"
int _tmain(int argc, _TCHAR* argv[]) {
    std::cout << "3 + 4 = " << add(3, 4) << std::endl;
    system("pause");
    return 0;
}

预处理给您:

// Contents of stdafx.h
// Contents of iostream
#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif
#ifndef ADD_H
#define ADD_H
int add(int x, int y);
#endif
int _tmain(int argc, _TCHAR* argv[]) {
    std::cout << "3 + 4 = " << add(3, 4) << std::endl;
    system("pause");
    return 0;
}

第一个#ifndef将是过程,它将看到ADD_H尚未定义,并且在#endif保留之前,所有内容均未定义。然后定义ADD_H

然后处理第二个#ifndef,但此时已定义ADD_H,因此所有内容都可以直到#endif被丢弃为止。

这很重要,因为对函数(以及许多其他事情)具有多个定义会给您带来错误。

IDE依次编译每个.cpp文件以生成该特定文件的对象文件(机器代码)。

完成所有操作,它将写入位以形成可执行文件。IDE知道需要与什么联系。

这是一些简单的anwser

相关内容

  • 没有找到相关文章

最新更新