Где должны находиться операторы #include? - PullRequest
4 голосов
/ 25 ноября 2010

Как возвращающийся новичок в C ++, я пытаюсь отсортировать методологию #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 наследуется от класса Father, он должен включить его в файл * .h и * .cpp
  • Если ClassA использует ClassB (и имеет ClassBпеременная, объявленная в области видимости класса), имеет class ClassB; декларацию в ClassA.h и #include "ClassB.h" в ClassA.cpp
  • Если ClassA использует StructC (и имеет переменную StructC, объявленную в области видимости класса), он должен включать его как в ClassA.h, так и в ClassA.cpp
  • Если ClassA использует ClassD или StructE, но только в файле ClassA.cpp, то он должен включать их только там

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

ОБНОВЛЕНИЯ:

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

Ответы [ 5 ]

9 голосов
/ 25 ноября 2010

Вот рекомендации, которым я лично следую:

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

Что касается других аспектов:

  • #pragma нестандартно, предпочитают включать охрану
  • Сохранитьпомните, что вы никогда не должны пересылать объявления стандартных типов: если в вашем заголовочном файле появляется std::string, у вас есть от до #include <string>
  • Если в итоге вы получите файл заголовка, который включаетмиллионов других файлов, возможно, вы захотите изучить идиому pimpl , чтобы уменьшить зависимости (эта статья также содержит несколько других рекомендаций, касающихся заголовка fилс).
1 голос
/ 25 ноября 2010

Не используйте прямое объявление 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 
}; 
1 голос
/ 25 ноября 2010

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

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

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

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

0 голосов
/ 25 ноября 2010

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

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

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

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

0 голосов
/ 25 ноября 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...