readonly
ключевое слово используется для объявления членской переменной константа, но позволяет значению быть вычисленным во времени выполнения. Это отличается от константы, объявленной с const
модификатор, который должен иметь его набор значений во время компиляции. Используя readonly
можно установить значение поля или в объявлении, или в конструкторе объекта, которого поле является членом.
Также использование это, если Вы не хотите должными быть перекомпилировать внешние DLLs, которые ссылаются на константу (так как это заменяется во время компиляции).
В вашем примере да, 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. Излишние зависимости вызывают много головной боли:
Основное правило: #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
ит.
Заголовок исходного файла должен определять интерфейс, необходимый пользователям кода для его точного использования. Он должен содержать все, что им нужно для использования интерфейса, но ничего лишнего. Если им нужна возможность, предоставляемая 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' не должны сильно беспокоиться об этом.
Чтобы ответить на последний вопрос : ясно, что заголовки, необходимые для реализации, но не необходимые интерфейсу, должны быть включены только в исходный код реализации, а не в заголовок. То, что использует реализация, не имеет отношения к пользователю, а эффективность компиляции и сокрытие информации требуют, чтобы заголовок предоставлял пользователям заголовка только минимально необходимую информацию.
Включая все предварительный ввод в заголовки в C ++ может привести к тому, что время компиляции будет explode
Лучше инкапсулировать и пересылать объявления как можно больше. Предварительные объявления дают достаточно подсказок о том, что требуется для использования класса. Однако вполне приемлемо иметь стандартные включения (особенно шаблоны, поскольку они не могут быть объявлены вперед).
Мои комментарии могут быть не прямым ответом на ваш вопрос, но полезными.
IOD / IOP рекомендует помещать меньше заголовков в заголовки INTERFACE, насколько это возможно, основные преимущества этого:
на IOD / IOP, интерфейсы должны быть помещены только в заголовки .h / .hxx. вместо этого включите заголовки в ваш .c / .cpp.
Мои правила для файлов заголовков:
В файле заголовка только классы #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 (но сделайте это только в случае крайней необходимости).
В исходном тексте файлы заголовков размещаются в порядке от наиболее специфичного к наименее конкретному.
Но не включайте то, что вам явно не нужно.
Итак, в этом случае вы должны включить:
Аргумент здесь в том, что если Clone.h требуется карта (а Plop требуется карта), и вы помещаете файлы заголовков C ++ ближе к началу списка, затем вы скрываете тот факт, что Clone.h нуждается в карте, поэтому вы не можете добавлять его в Clone.h.
Всегда используйте защиту заголовков
#ifndef <NAMESPACE1>_<NAMESPACE2>_<CLASSNAME>_H
#define <NAMESPACE1>_<NAMESPACE2>_<CLASSNAME>_H
// Stuff
#endif
PS: я не предлагаю использовать несколько вложенных пространств имен. Я просто демонстрирую, как у меня получается, если я это делаю. Я обычно помещаю все (кроме основного) в пространство имен. Вложение будет зависеть от ситуации.
Избегайте объявления 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;