2015-04-23 3 views
1

У меня есть класс Angle с оператором overloaded < < и двумя файлами cpp, в которые включен заголовочный файл. У меня есть инструкция #ifndef, чтобы предотвратить включение файла несколько раз. Однако кажется, что он включен несколько раз, так как я получил ошибку, которую оператор < < был определен несколько раз. Затем я добавил приложение #warning, чтобы увидеть, когда этот файл включен. В выводе компилятора видно, что #warning обрабатывается дважды. Если я переведу определение в файл cpp, он, очевидно, работает, но я по-прежнему считаю поведение в этом случае странным.Файл заголовка включен дважды, несмотря на #ifndef

По-моему, компилятор не должен обрабатывать файл заголовка дважды. Есть ли причина такого поведения?

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" 

Я компиляции с followi Команда нг:

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 
+1

Это хорошее время, чтобы прочитать на "перевода единиц" и, как C++ код на самом деле скомпилирован. В принципе, каждый .cpp-файл скомпилирован независимо, тогда линкера отвечает за совпадение функций * определений * (которые обычно могут выполняться только один раз) с их * обычаями *, которые скомпилированы на основе соответствующих * деклараций *. – Cameron

+0

См. Также: http: // stackoverflow.com/questions/14425262/why-include-guard-do-not-prevent-multiple-function-определения – chris

ответ

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; 
} 
+0

Да, я получил это уже, как писал в своем описании. Но мне кажется странным, что это не работает наоборот. Если я использую только библиотеку заголовка, я могу включить ее также в несколько файлов cpp, не сталкиваясь с этой проблемой. – ipa

+0

Файл заголовка действительно включен дважды, в Angle.cpp и main.cpp. Включить защитные ограждения защищать от включения одного и того же файла дважды в одну и ту же блок переводов. Обычно это нормально, потому что вам разрешено многократно объявлять переменную. Проблема возникает, когда вы пытаетесь определить переменную. Это может произойти только один раз. –

+0

Хорошо, я не знал о переводе единиц перевода. В этом случае это имеет смысл. благодаря – ipa

0

Это совершенно нормально для заголовка файла должны быть включены несколько раз при компиляции. #ifndef будет только сбрасывать множество включений в то же самое процесс компиляции, то есть тот же файл .cpp.

Этап компиляции обычно скомпилирует несколько файлов .cpp до промежуточной формы, которые позже связаны друг с другом в одну библиотеку или исполняемый файл.

Если вам нужно определить функцию один раз на цель, этот код необходимо переместить в один файл .cpp. Включение в заголовок может привести к дублированию.