2010-11-25 3 views
4

Как возвращающийся новичок на C++, я пытаюсь сортировать методологию #include.Где должно быть указано #include?

Я следую определенному набору руководств, которые я подробно описываю ниже в следующем примере. Пока это сработало для меня (весь проект держит компиляцию :)), но я беспокоюсь, что в будущем я могу столкнуться с проблемами, поэтому мои вопросы - - это правильная методология? Есть ли лучший? Какова основная логика, которая объясняет это?

Рассмотрим следующий пример:

Father.h

#pragma once 
class Father 
{ 
    // Some implementation 
}; 

ClassA.h

#pragma once 
#include "Father.h" 
#include "StructC.h" 
class ClassB; 
class ClassA : public Father 
{ 
    StructC struct_c_obj; 
    ClassB class_b_obj; 
    // Some implementation 
}; 

ClassA.cpp

#include "Father.h" 
#include "ClassB.h" 
#include "StructC.h" 
// Some implementation 

ClassB.h и ClassB.cpp
Класс без включает

StructC.h

struct StructC { 
    // Some implementation 
}; 

Я следую этим правилам:

  • Все * .h возглавляет #pragma once декларация
  • Если ClassA наследует от класса Отца, он должен включить его в обеих * .h и * .cpp файл
  • Если ClassA использует ClassB (и имеет переменный ClassB объявленные в области видимости класса), он имеет class ClassB; decleration в ClassA.h и #include "ClassB.h" в ClassA.cpp
  • Если ClassA использует StructC (и имеет StructC переменную, объявленную в области видимости класса), он должен включить его в обоих ClassA.h и ClassA.cpp
  • If ClassA использует ClassD или StructE, но только в файле ClassA.cpp, тогда он должен включать их только там

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

ОБНОВЛЕНИЕ:

  • Как некоторые уже написано ниже, у меня есть ошибка в примере - вы можете использовать вышеописанную декларацию ClassB в ClassA только если ClassA имеет указатель или ссылку на ClassB а не если он имеет простой элемент данных ClassB.

ответ

9

Эти принципы я лично следовать:

  • Предпочитают вперед декларации вместо включает в себя всякий раз, когда это возможно. В вашем случае ClassA содержит ClassB, поэтому требуется #include "ClassB.h". Если бы тип ClassB отображался только в виде указателя или ссылки, то было бы достаточно прямой ссылки
  • Сделать заголовочный файл «самодостаточным»: компиляция никогда не должна зависеть от порядка включений, а включенный файл должен включать/пересылать объявить все это должно быть разобрано
  • чтобы убедиться, что предыдущее руководство уважает, всегда включает в себя ClassA.h первых в ClassA.cpp и использовать произвольный порядок на следующем включает в себя (я использую алфавитный род)

что касается другие аспекты:

  • #pragma не является стандартом, предпочитают include guards
  • Имейте в виду, что вы никогда не должны вперед объявить стандартные типы: если std::string появляется в файле заголовка, вы имеют к #include <string>
  • Если вы в конечном итоге с файлом заголовка который включает миллион других файлов, вы можете посмотреть в pimpl idiom для уменьшения зависимостей (эта статья также содержит несколько других рекомендаций относительно файлов заголовков).
+0

У меня есть два вопроса относительно вашего ответа: 1) почему мы должны предпочесть декларации вперед? 2) Я пытаюсь понять, почему бы не использовать #pragma, почему важно, чтобы это не было в стандарте? – Delashmate

+1

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

+2

+1. Это хороший ответ. Я бы добавил, что порядок включает в себя: 'ClassA.h' сначала, затем любые' ClassB.h' или другие ваши собственные, затем заголовки Boost или другие заголовки библиотек и, наконец, стандартные заголовки библиотек ('string', 'vector' и т. д.). Следуя этому порядку, вы гарантируете, что ваши собственные заголовки самодостаточны. –

0

Одна вещь, которую я получил от Python (потому что это абсолютное требование) - это «импортировать (включать) ее в модули, в которых вы ее используете».Это позволит вам избавиться от неприятностей, когда дело доходит до определения или отсутствия определений.

1

#pragma once является нестандартным (но широко поддерживается), поэтому вы можете не использовать вместо этого #ifdef.

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

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

Сказав, что, я думаю, что ClassA.h фактически должно включать в себя ClassB.h, потому что любой пользователь ClassA.h (предположительно использовать ClassA) будет иметь ClassB.h. Ну, если это что-то вроде распределения.

0

Обычно я не использую pragma once, потому что прагмы не являются стандартными. Возможно, вам придется переносить свой код на другой компилятор, где он не определен, и вам придется переписывать каждый из них с #ifndef ... #define идиомой.

Это потому, что я иду прямо с #ifndef ... #define.

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

Если это так, я принимаю идиому Pimpl, которую вы можете найти здесь here (см. Пример на C++). В любом случае, если размер проекта не такой большой, я думаю, что ваш подход правильный.

1

Не используйте форвардное объявление ClassB, когда ClassA имеет член данных этого типа. Это нормально, чтобы использовать его, когда у него есть указатели на ClassB, как:

#pragma once 
#include "Father.h" 
#include "StructC.h" 
class ClassB; 
class ClassA : public Father 
{ 
    StructC struct_c_obj; 
    ClassB *class_b_obj; 
    // Some implementation 
}; 
Смежные вопросы