Модераторы: bsa
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Ошибка redefinition of '...', проблема множественного включения заголо 
:(
    Опции темы
JackYF
Дата 29.12.2007, 20:00 (ссылка) |    (голосов:2) Загрузка ... Загрузка ... Быстрая цитата Цитата


полуавантюрист
****


Профиль
Группа: Участник
Сообщений: 5814
Регистрация: 28.8.2004
Где: страна тысячи озё р

Репутация: 6
Всего: 162



Проблема множественного включения заголовочных файлов(headers)


Пример:

Пускай программа состоит из трёх файлов - main.cpp, a.h, b.h.
a.h:
Код

struct point
{
    int x;
    int y;
};

void func_a();


b.h:
Код

#include "a.h"

void func_b(point* pnt);


main.cpp:
Код

#include "a.h"
#include "b.h"

int main()
{
    point* p;
    func_a();
    func_b(p);

    return 0;
}

В заголовочном файле a.h описывается структура point и объявляется функция func_a.
В заголовочном файле b.h включается заголовочный файл a.h и объявляется функция func_b.
В основном файле программы main.cpp подключаются заголовочные файлы a.h и b.h, в функции main() поочерёдно вызываются функции func_a, func_b, затем программа завершается.

Проблема:
Программа не компилируется. Компилятор выдаст ошибку, например так:
Код
a.h:1: error: redefinition of ‘struct point’

Почему так происходит?
Директива компилятора #include говорит компилятору о том, что содержимое указанного файла нужно вставить в данное место.
Описание структуры point, итого, вставится в main.cpp два раза: 1-й раз - явно - при включении файла a.h, второй раз - неявно - при включении файла b.h, так как файл b.h тоже включает в себя содержимое файла a.h.
Код главного модуля сначала преобразуется в такое:
Код

struct point
{
    int x;
    int y;
};

void func_a();

#include "a.h"

void func_b(point* pnt);

int main()
{
    point* p;
    func_a();
    func_b(p);

    return 0;
}

После чего снова обрабатывается #include и получается:
Код

struct point
{
    int x;
    int y;
};

void func_a();

struct point
{
    int x;
    int y;
};

void func_a();


void func_b(point* pnt);

int main()
{
    point* p;
    func_a();
    func_b(p);

    return 0;
}

В этом, итоговом, коде присутствуют два определения struct point. Это и служит причиной выдачи ошибки компилятором.

Решение:
Использовать условные директивы компилятора #ifndef...#define...#endif.
Их также называют "стражами включения".

Код всех заголовочных файлов нужно обрамлять:
Код

#ifndef идентификатор_файла
#define идентификатор_файла

// здесь всё, что было раньше в заголовочном файле

#endif

Идентификатором файла может быть любое правильной имя переменной в С/С++.
Однако, принято давать этим иденфикаторам имена, состоящие из больших букв латинского алфавита и подчёркиваний, логические связанные с именем файла.
К примеру, для файла abcd.h можно взять идентификаторы ABCD_H, FILE_ABCD_H, ABCD_H_INCLUDED
Важно: идентификаторы заголовочных файлов в пределах одной программы(одного проекта) должны быть разными, иначе Вы получите обратный результат(не множественное включения, а отсутствие включения).

Исправленный заголовочный файл a.h:
Код

#ifndef A_H_INCLUDED // если этот файл ещё не включён
#define A_H_INCLUDED // включить этот файл

struct point
{
    int x;
    int y;
};

void func_a();
#endif


Работа этих директив в данном случае заключается в том, что в первой строчке проверяется, не определено ли A_H_INCLUDED. Если определно, значит, содержимое данного файла уже было включено, и компилятору не надо обрабатывать содержимое файла повторно. Если же нет, то определяем A_H_INCLUDED и обрабатываем дальше заголовочный файл.

В этом случае, код модуля с учётом произведённых #include имеет вид:
Код

#ifndef A_H_INCLUDED // идентификатор ещё не определён
#define A_H_INCLUDED // определяем

struct point
{
    int x;
    int y;
};

void func_a();
#endif

#ifndef A_H_INCLUDED // а здесь идентификатор уже определён
#define A_H_INCLUDED // поэтому эта и все остальные строки до #endif удаляются

struct point
{
    int x;
    int y;
};

void func_a();
#endif

void func_b(point* pnt);

int main()
{
    point* p;
    func_a();
    func_b(p);

    return 0;
}

Итого получается следующее:
Код

struct point
{
    int x;
    int y;
};

void func_a();

void func_b(point* pnt);

int main()
{
    point* p;
    func_a();
    func_b(p);

    return 0;
}

Это и есть желаемый нами код.

Данное решение работает на всех известных компиляторах С/С++.

Замечания:
1. В данном примере файл b.h тоже желательно должен быть обрамлён.
2. Не начинайте имя идентификатора с подчёркивания, это крайне не рекомендуется стандартом С++.


Другие решения.
Некоторые компиляторы С/С++ предоставляют ещё одно решение проблемы.
Для этого необходимо включить в начало каждого заголовочного файла директиву
Код

#pragma once

Данная директива имеет тот же эффект, что и вышеприведенное решение, но для её применения потребуется компилятор, поддерживающий это расширение. В число таких компиляторов входят: компилятор из MS Visual Studio (MSVC), компилятор g++ из GCC.

Назад к FAQ

Это сообщение отредактировал(а) bsa - 26.7.2011, 11:02


--------------------
Пожаловаться на меня как модератора можно здесь.
PM MAIL Jabber   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
1 Пользователей читают эту тему (1 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.0636 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.