我有一个类角度,<<运算符重载,还有两个包含头文件的 cpp 文件。我有一个 #ifndef 语句来防止多次包含该文件。但是,它似乎被多次包含,因为我收到一个错误,即多次定义运算符<<。然后,我添加了一个 #warning,以查看何时包含该文件。在编译器输出中,可以看到 #warning 被处理了两次。如果我将定义移动到 cpp 文件,它显然可以工作,但我仍然觉得这种情况下的行为很奇怪。
在我看来,编译器不应该两次处理头文件。这种行为有什么原因吗?
主.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;
}
角度.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
角度.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
问题不在于标头被包含两次,而在于该函数被定义了两次。在.cpp文件中定义它,并将 extern 添加到标头中的前向声明中,一切都应该可以正常工作。问题是每次包含标头时,它都会创建函数的新实现,并且您会收到一个链接器错误,它不知道要使用哪个链接器错误。这就是为什么你不应该在标头中实现全局函数,而是应该在.cpp中实现全局函数。
您的 Angle.h 将具有:
extern std::ostream& operator << (std::ostream& os, const Angle& obj);
而角度.cpp将具有:
std::ostream& operator << (std::ostream& os, const Angle& obj){
os << obj.getValue();
return os;
}
编译时多次包含头文件是完全正常的。#ifndef
只会在同一编译过程中隔离多个包含,即同一个.cpp
文件。
编译阶段通常将多个.cpp
文件编译为中间形式,然后链接在一起形成单个库或可执行文件。
如果需要为每个目标定义一次函数,则必须将该代码移动到单个.cpp
文件中。将其包含在标头中可能会导致重复。