Зачем помещать весь заголовок в защитные токены? - PullRequest
3 голосов
/ 16 декабря 2011

C и C ++ различают объявления и определения.

Вы можете объявить символ много раз, но вы можете определить его только один раз. Узнав об этом, у меня появилась идея поместить декларации вне охраны, тогда как определения внутри охраны:

// declarations

int foo(int x, int y);
int bar(int x, int y);

extern double something;

class X;

#ifndef _MY_HEADER_H_
#define _MY_HEADER_H_

#include "otherheader1.h"
#include "otherheader2.h"

// definitions of structures, classes.

class X
{
    // blah blah...
};

#endif

Таким образом, я могу включать свои заголовки в любом порядке. И, вероятно, круговые зависимости не будут проблемой.

Так зачем защищать весь заголовок токенами защиты, если мы можем размещать объявления снаружи?

Мое обоснование было следующим:

Вы, вероятно, часто сталкиваетесь с проблемами, когда два заголовка так или иначе ссылаются друг на друга. Обычно вы получаете необъявленную ошибку символа, и ваш первый рефлекс - включить необходимые заголовки. Но когда два ваших заголовка включают друг друга, вы получаете загадочные ошибки.

хиджры:

#ifndef A_H
#define A_H

#include "b.h"

class A {B *b;}       

#endif

b.h

#ifndef B_H
#define B_H

#include "a.h"

class B {A *a;}       

#endif

Когда в b.cpp вы включаете b.h, вы получаете ошибку в a.h, что B не объявлен, но включен заголовок. (Это хороший момент.)

Это потому, что охранники заголовка не будут гнездиться:

#ifndef B_H
#define B_H

#ifndef A_H
#define A_H

// B_H already defined no include here.

class A {B *b;}       

#endif

class B {A *a;}       

#endif

Если вы размещаете объявления вне охраны, вы можете предотвратить это:

class B; // in b.h

#ifndef B_H
#define B_H

class A; // in a.h

#ifndef A_H
#define A_H

class B; // again from b.h
// B_H already defined no include here, no redefinition.

class A {B *b;}       

#endif

class B {A *a;}       

#endif

Здесь нет проблем.

ОБНОВЛЕНИЕ: вставьте заголовок в охранники (извините, это была ошибка).

Ответы [ 5 ]

4 голосов
/ 16 декабря 2011

Вы упускаете половину истории, когда думаете только о «декларациях».В C ++ также есть понятие «определения классов», которые представляют собой третий, новый тип животных - это одновременно и определение (класса), и объявление (членафункции).

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

Теперь представьте, что у вас есть некоторый служебный класс Foo в foo.hpp, и у вас есть два независимых модуля a.hpp и b.hpp, для обоих из которых требуется Foo.Ваша основная программа должна включать a.hpp и b.hpp, но теперь вы пытаетесь включить foo.hpp, и, следовательно, определение класса Foo, дважды.

Введите include guard.

2 голосов
/ 16 декабря 2011

Потому что это позволяет вам #include заголовок несколько раз, не боясь конфликтов.

Хотя это не является необходимым, если у вас есть один уровень вложенности, он необходим, если у вас есть несколько (подумайте о включении h1, затем включите h2, который включает в себя h1, потому что это необходимо).

1 голос
/ 16 декабря 2011

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

1 голос
/ 16 декабря 2011

Ваша система не защищает от циклического включения.Например:

Заголовок A:

#include "B.h"
#ifndef A_H_INCLUDED
#define A_H_INCLUDED
// ...
#endif // A_H_INCLUDED

Заголовок B:

#include "A.h"
#ifndef B_H_INCLUDED
#define B_H_INCLUDED
// ...
#endif // B_H_INCLUDED

Исходный файл:

#include "A.h" // includes B, which includes A, which includes B, ...
1 голос
/ 16 декабря 2011

нет необходимости, если это строго защита заголовка - объявление уже видно, если включено многократное включение.

другая причина против этого заключается в том, что объявления вне строгой защиты заголовков могут отключить оптимизацию компилятора для многократно включенных заголовков (то есть он будет открывать заголовок больше раз).

...