Подтвердить что ты не робот

Организация файлов заголовков классов С++

Каковы рекомендации по кодированию и каталогу на С++, которые вы предлагаете для людей, которым приходится иметь дело с большим количеством взаимозависимых классов, распространяемых по нескольким исходным и заголовочным файлам?

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

4b9b3361

Ответ 1

Некоторые общие рекомендации:

  • Соедините свои интерфейсы с реализациями. Если у вас есть foo.cxx, все, что определено в нем, было лучше объявлено в foo.h.
  • Убедитесь, что каждый заголовочный файл # включает все остальные необходимые заголовки или форвардные объявления, необходимые для независимой компиляции.
  • Сопротивляйтесь соблазну создать заголовок "все". Они всегда беспокоят дорогу.
  • Поместите набор связанных (и взаимозависимых) функций в один файл. Java и другие среды поощряют использование одного класса для каждого файла. С С++ вам часто нужен один набор классов для каждого файла. Это зависит от структуры вашего кода.
  • Предпочитайте форвардную декларацию над #include, когда это возможно. Это позволяет разбить циклические зависимости заголовка. По существу, для циклических зависимостей между отдельными файлами вы хотите, чтобы график зависимости от файлов выглядел примерно так:
    • A.cxx требуется A.h и B.h
    • B.cxx требуется A.h и B.h
    • A.h требуется B.h
    • B.h является независимым (и forward-declares классы, определенные в A.h)

Если ваш код предназначен для библиотеки, потребляемой другими разработчиками, необходимо предпринять следующие дополнительные шаги:

  • При необходимости используйте понятие "частные заголовки". То есть заголовочные файлы, требуемые несколькими исходными файлами, но никогда не требуемые публичным интерфейсом. Это может быть файл с общими встроенными функциями, макросами или внутренними константами.
  • Отделите свой публичный интерфейс от частной реализации на уровне файловой системы. Я обычно использую подкаталоги include/ и src/ в моих проектах на C или С++, где include/ имеет все мои публичные заголовки, а src/ - все мои источники. и частные заголовки.

Я бы рекомендовал найти копию книги Джона Лакоса "Крупномасштабная разработка программного обеспечения на C++". Это довольно внушительная книга, но если вы просто просмотрите некоторые из своих обсуждений по физической архитектуре, вы узнаете много.

Ответ 2

Ознакомьтесь со стандартами кодирования C и С++ в Центре космических полетов NASA Годдард. Единственное правило, которое я специально отметил в стандарте C и которое было принято в моем собственном коде, - это тот, который обеспечивает "автономный" характер файлов заголовков. В файле реализации xxx.cpp для заголовка xxx.h убедитесь, что xxx.h - это первый заголовок, включенный. Если заголовок не является автономным в любое время, компиляция завершится неудачей. Это красивое и эффективное правило.

Единственный раз, когда он терпит неудачу, - это если вы портируете между машинами, а заголовок xxx.h включает, скажем, <pqr.h>, но <pqr.h> требует средств, которые становятся доступными заголовком <abc.h> на оригинальная платформа (поэтому <pqr.h> включает <abc.h>), но средства не доступны <abc.h> на другой платформе (они находятся в def.h вместо этого, но <pqr.h> не включает <def.h>). Это не является ошибкой правила, и проблема легче диагностируется и исправляется, если вы следуете правилу.

Ответ 4

Ответ Tom - отличный!

Единственное, что я хотел бы добавить, - никогда не "использовать объявления" в файлах заголовков. Они должны разрешаться только в файлах реализации, например. foo.cpp.

Логика для этого хорошо описана в превосходной книге "Ускоренный С++" (ссылка Amazon - дезинфицирована для script kiddie link nazis )

Ответ 5

Еще один момент в дополнение к остальным здесь:

Не включайте никаких частных определений в включенном файле. Например. Любые определение, которое используется только в xxx.cpp должен находиться в xxx.cpp, а не xxx.h.

Кажется очевидным, но я вижу это часто.

Ответ 6

Я бы хотел добавить одну очень хорошую практику (как на C и С++), которая часто оставляется:

foo.c

#include "foo.h" // always the first directive

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

Если в любой момент вам нужно поставить что-то перед включением заголовка (за исключением комментариев, конечно), то, скорее всего, вы делаете что-то неправильно. Если вы действительно не знаете, что делаете... что приводит к еще одному решающему правилу = > комментируйте свои хаки!