尽管#ifndef,头文件包含两次

时间:2015-04-23 21:45:59

标签: c++

我有一个类角度与<<运算符重载和两个包含头文件的cpp文件。我有一个#ifndef语句来防止文件被多次包含。但是,似乎它被包含多次,因为我得到了一个错误,即运算符<<被定义了多次。然后我添加了一个#warning satement来查看文件的包含时间。在编译器输出中可以看到#warning被处理两次。如果我将定义移动到cpp文件,它显然有效,但我仍然觉得这种情况下的行为很奇怪。

在我看来,编译器不应该两次处理头文件。这种行为有原因吗?

的main.cpp

#include <iostream>
#include <cmath>
#include <cstdlib>
#include "Angle.h"
using namespace std;

int main() {
    Angle a = (Angle)(M_PI/4);
    cout << a << endl;
    return EXIT_SUCCESS;
}

Angle.h

#ifndef ANGLE_ANGLE_H
#define ANGLE_ANGLE_H
#include <iostream>

class Angle {
private:
    double value;
public:
    explicit Angle (double value) {this->value = value; }
    double getValue() const {return this->value;}

    // Operators
    operator const double() const {
        return this->value;
    }
};

#warning INCLUDING_ANGLE_H

std::ostream& operator << (std::ostream& os, const Angle& obj){
    os << obj.getValue();
    return os;
}
#endif //ANGLE_ANGLE_H

Angle.cpp

#include "Angle.h"

我正在使用以下命令进行编译:

g++ main.cpp Angle.cpp -o angle

出现以下错误:

In file included from main.cpp:5:0:
Angle.h:19:2: warning: #warning INCLUDING_ANGLE_H [-Wcpp]
 #warning INCLUDING_ANGLE_H
  ^
In file included from Angle.cpp:1:0:
Angle.h:19:2: warning: #warning INCLUDING_ANGLE_H [-Wcpp]
 #warning INCLUDING_ANGLE_H
  ^
/tmp/cci53Hrd.o: In function `operator<<(std::ostream&, Angle const&)':
Angle.cpp:(.text+0x0): multiple definition of `operator<<(std::ostream&, Angle const&)'
/tmp/ccBbwtlD.o:main.cpp:(.text+0x0): first defined here
collect2: error: ld returned 1 exit status

2 个答案:

答案 0 :(得分:2)

问题不在于标题包含两次,而是函数定义了两次。在.cpp文件中定义它,并在标题中的前向声明中添加extern,一切都应该有效。问题是,每次包含头时,它都会创建一个新的函数实现,并且您将收到一个链接器错误,它不知道要使用哪个。这就是为什么你不应该在头文件中实现全局函数,而是在.cpp。

中实现

你的Angle.h将有:

extern std::ostream& operator << (std::ostream& os, const Angle& obj);

而Angle.cpp将具有:

std::ostream& operator << (std::ostream& os, const Angle& obj){
  os << obj.getValue();
  return os;
}

答案 1 :(得分:0)

编译时多次包含头文件是完全正常的。 #ifndef只会阻止相同编译过程中的多个包含,即相同的.cpp文件。

编译阶段通常将多个.cpp文件编译为中间形式,稍后将其链接到一个库或可执行文件中。

如果您需要为每个目标定义一次该函数,则必须将该代码移动到单个.cpp文件中。将其包含在标题中可能会导致重复。