C ++:理解头文件&带有简单附加示例的头部防护装置

时间:2014-01-31 18:54:31

标签: c++ header header-files include-guards

我无法理解标题和标题后卫。我已经阅读了其他问题及其答案,但我仍然无法在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   函数中引用的add(int,int)“(?add @@ YAHHH @ Z)   _wmain c:\ Users \ Danny \ documents \ visual studio 2013 \ Projects \ Addition Program \ main \ main.obj main

     

错误2错误LNK1120:1未解决   外部c:\ users \ danny \ documents \ visual studio   2013 \ Projects \ Addition Program \ Debug \ main.exe main


1。标题和标题保护如何工作?我看到如何#including add.h,它使main.cpp知道add(int x, int y)的声明,但是它如何找到它的定义?


2。我的代码中出了什么问题?

我的代码现在正在编译。我的代码没有编译的原因是因为我一直在文件&gt;新&gt;文件... 将文件添加到我的项目中,而不是通过解决方案资源管理器<的源文件页眉文件部分添加文件/ strong>在Visual Studio中。我还需要将#include "stdafx.h添加到a​​dd.cpp文件中。

2 个答案:

答案 0 :(得分:5)

以这种方式思考:每个.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的所有内容都将被丢弃。

这非常重要,因为对函数(以及许多其他事情)进行多次定义会给您带来错误。

答案 1 :(得分:1)

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

完成所有这些后,它会将这些位写入以形成可执行文件。 IDE知道什么需要与什么相关联。

这是一个有点简单的anwser