Стиль заголовка #include

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

Также использование это, если Вы не хотите должными быть перекомпилировать внешние DLLs, которые ссылаются на константу (так как это заменяется во время компиляции).

8
задан Jonathan Leffler 28 October 2009 в 01:24
поделиться

6 ответов

В вашем примере да, bar.h должен включать #include blah.h. Таким образом, если кто-то изменит foo так, что ему не нужно blah, изменение не сломает bar.

Если blah.h нужен в foo.c, но не в foo.h, то его не следует включать в foo.h. Многие другие файлы могут #include foo.h, а другие файлы могут #include их . Если вы #include blah.h в foo.h, то вы сделаете все эти файлы ненужными зависимыми от blah.h. Излишние зависимости вызывают много головной боли:

  • Если вы измените blah.h, все эти файлы придется перекомпилировать.
  • Если вы хотите изолировать один из них (скажем, чтобы перенести его в другой проект или построить на его основе модульный тест), вы должны взять с собой blah.h.
  • Если в одном из них есть ошибка, вы не можете исключить blah.h как причину, пока не проверите .
  • Если вы достаточно глупы, чтобы иметь что-то вроде макроса в blah.h ... ну, ничего, в таком случае для вас нет надежды.
10
ответ дан 5 December 2019 в 10:42
поделиться

Основное правило: #include любые заголовки, которые вы действительно используете в своем коде. Итак, если мы говорим:

// foo.h
#include "utilities.h"
using util::foobar;

void func() {
    foobar();
}

// bar.h
#include "foo.h"
#include "utilities.h"
using util::florg;

int main() {
    florg();
    func();
}

Где bar.h дважды использует инструменты из заголовка, то вы должны #include его, даже если у вас нет к. С другой стороны, если bar.h не требует никаких функций из utilities.h , то даже если foo.h включает его, не #include ит.

3
ответ дан 5 December 2019 в 10:42
поделиться

Заголовок исходного файла должен определять интерфейс, необходимый пользователям кода для его точного использования. Он должен содержать все, что им нужно для использования интерфейса, но ничего лишнего. Если им нужна возможность, предоставляемая xyz.cpp, то все, что требуется пользователю, это #include "xyz.h" .

Как 'xyz.h' обеспечивает эту функциональность, в значительной степени зависит от разработчик "xyz.h". Если для этого требуются средства, которые можно указать только путем включения определенного заголовка, тогда «xyz.h» должен включать этот другой заголовок. Если он может избежать включения определенного заголовка (путем прямого определения или любых других чистых средств), он должен это сделать.

В этом примере мое кодирование, вероятно, будет зависеть от того, находится ли заголовок 'foo.h' под контролем тот же проект, что и «мля. h 'заголовок. Если так, то я бы, вероятно, не стал делать явное второе включение; если нет, я мог бы включить его. Однако приведенные выше утверждения должны вынудить меня сказать: «Да, включите 'foo.h' на всякий случай».

В свою защиту я считаю, что стандарт C ++ позволяет включать любой из заголовков C ++, чтобы включать другие - в соответствии с требованиями реализации; это можно рассматривать как подобное. Проблема в том, что если вы включаете только «bar.h» и все же используете функции из «blah.h», то при изменении «bar.h», поскольку его коду больше не нужен «blah.h», тогда код пользователя, который использовался для компиляции (случайно), теперь не удается.

Однако, если пользователь напрямую обращался к средствам 'blah.h', тогда пользователь должен был включить 'blah.h' напрямую. Обновленный интерфейс кода в строке. h 'больше не нужен' blah.h ', поэтому любой код, который использовал только интерфейс для' bar.h ', должен работать нормально. Но если в коде тоже использовался «blah.h», то он должен был включать его напрямую.

Я подозреваю, что Закон Деметры также следует учитывать - или может рассматриваться как влияющий на это. По сути, 'bar.h' должен включать заголовки, которые необходимы, чтобы заставить его работать, прямо или косвенно - и потребители 'bar.h' не должны сильно беспокоиться об этом.

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

3
ответ дан 5 December 2019 в 10:42
поделиться

Включая все предварительный ввод в заголовки в C ++ может привести к тому, что время компиляции будет explode

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

0
ответ дан 5 December 2019 в 10:42
поделиться

Мои комментарии могут быть не прямым ответом на ваш вопрос, но полезными.

IOD / IOP рекомендует помещать меньше заголовков в заголовки INTERFACE, насколько это возможно, основные преимущества этого:

  1. ] меньше зависимостей;
  2. меньший объем символов времени компоновки;
  3. более быстрая компиляция;
  4. меньший размер конечных исполняемых файлов, если заголовок содержит статические определения функций в стиле C и т. Д.

на IOD / IOP, интерфейсы должны быть помещены только в заголовки .h / .hxx. вместо этого включите заголовки в ваш .c / .cpp.

0
ответ дан 5 December 2019 в 10:42
поделиться

Мои правила для файлов заголовков:

Правило №1

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

--Plop.h
#include "Base.h"
#include "Stop.h"
#include <string>

class Plat;
class Clone;

class Plop: public Base
{
    int           x;
    Stop          stop;
    Plat&         plat;
    Clone*        clone;
    std::string   name;
};

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

Правило №2

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

Итак, в этом случае вы должны включить:

  • Plop.h (Самый конкретный).
  • Clone.h / Plat.h (используется непосредственно в классе)
  • Заголовочные файлы C ++ (от них могут зависеть Clone и Plat)
  • Заголовочные файлы C

Аргумент здесь в том, что если Clone.h требуется карта (а Plop требуется карта), и вы помещаете файлы заголовков C ++ ближе к началу списка, затем вы скрываете тот факт, что Clone.h нуждается в карте, поэтому вы не можете добавлять его в Clone.h.

Правило № 3

Всегда используйте защиту заголовков

#ifndef  <NAMESPACE1>_<NAMESPACE2>_<CLASSNAME>_H
#define  <NAMESPACE1>_<NAMESPACE2>_<CLASSNAME>_H

// Stuff

#endif

PS: я не предлагаю использовать несколько вложенных пространств имен. Я просто демонстрирую, как у меня получается, если я это делаю. Я обычно помещаю все (кроме основного) в пространство имен. Вложение будет зависеть от ситуации.

Правило № 4

Избегайте объявления using.
За исключением текущей области действия класса, над которым я работаю:

-- Stop.h
#ifndef THORSANVIL_XXXXX_STOP_H
#define THORSANVIL_XXXXX_STOP_H

namespace ThorsAnvil
{
    namespace XXXXX
    {

class Stop
{
};

     } // end namespace XXXX
} // end namespace ThorsAnvil

#endif

-- Stop.cpp
#include "Stop.h"
using namespace ThorsAnvil:XXXXX;
0
ответ дан 5 December 2019 в 10:42
поделиться
Другие вопросы по тегам:

Похожие вопросы: