Должны ли операторы #include и using повторяться в файлах заголовка и реализации (C ++)? - PullRequest
6 голосов
/ 27 апреля 2010

Я довольно новичок в C ++, но, насколько я понимаю, оператор #include, по сути, просто сбросит содержимое файла #included в расположение этого оператора. Это означает, что если в моем заголовочном файле есть несколько операторов '#include' и 'using', мой файл реализации может просто #include заголовочный файл, и компилятор не будет возражать, если я не повторю другие операторы .

А как же люди?

Моя главная проблема заключается в том, что если я не повторю операторы '#include', 'using', а также 'typedef' (теперь, когда я об этом думаю), он забирает эту информацию из файла, в котором он находится. используется, что может привести к путанице.

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

Ниже приведен пример:

ОБНОВЛЕНИЕ: мои прототипы функций для 'Unit' имеют строку, ostream и StringSet среди своих возвращаемых типов и параметров - я не включаю в файл заголовка ничего, что используется только в файле реализации. 1013 *

//Unit.h

#include <string>
#include <ostream>
#include "StringSet.h"

using std::string;
using std::ostream;

class Unit {

public:
    //public members with string, ostream and StringSet
    //in their return values/parameter lists
private:
    //private members
    //unrelated side-question: should private members
    //even be included in the header file?
} ;


//Unit.cpp

#include "Unit.h"

//The following are all redundant from a compiler perspective:
#include <string>
#include <ostream>
#include "StringSet.h"

using std::string;
using std::ostream;

//implementation goes here

Ответы [ 5 ]

6 голосов
/ 27 апреля 2010

A using-директива (using namespace std;) не должна находиться в заголовке, если она не содержится в функции. Это плохая практика. Маловероятно, что каждый пользователь вашего заголовка хочет получить безоговорочный поиск всего в данном пространстве имен; включение несвязанных заголовков может привести к неожиданным ошибкам и сбоям компиляции. Лично я избегаю директивы использования внутри функций по той же причине, но это обычно считается менее вредным.

Псевдоним типа (либо через typedef std::string string;, либо using string = std::string;) следует использовать осторожно. Определения типов имеют значение, поэтому вы никогда не должны объявлять его заново. Например, это ошибка:

typedef int   myint;
typedef float myint;

из-за конфликтующих типов.

A с помощью объявления (using std::string; или using std::memcpy;) делает символ доступным для поиска безусловного имени . Это очень полезно, если нужно правильно аргументно-зависимый поиск , что обычно не имеет значения, если вы не пишете библиотеку. Советы различаются в зависимости от того, вводите ли вы тип или функцию. Подумайте о использовании-объявлении с типами таким же образом, что и псевдоним типа : не имеет смысла иметь несколько определений под одним именем. С помощью функций все, что вы действительно делаете, - это расширяете разрешение перегрузки, чтобы включить еще несколько вещей (хотя обычно это не требуется).

// Finding multiple operator<< functions makes sense
using std::operator<<;
using mylib::operator<<;

// Finding multiple string classes does not make sense
using std::string;
using mylib::string;

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

3 голосов
/ 07 августа 2010
  • Включите в заголовок / источник только то, что вам действительно нужно (если предварительных объявлений достаточно и их достаточно, то вместо включения включается прямое объявление)
  • Не использовать оператор using в заголовках (если только внутри областей действия функций) ... Добавление using в заголовок загрязнит пространства имен всех источников, включая заголовок.
  • Вы должны убедиться, что каждый файл (заголовок исходного кода) содержит все, что ему нужно, и ничего более.

Вам не нужно заботиться о том, являются ли некоторые включения избыточными. Охрана заголовков и оптимизация прекомпиляторов помогут вам справиться с этим.

Вы должны иметь возможность манипулировать каждым файлом в отдельности.

Например, допустим, вы используете std::string в заголовке и в источнике, но, в качестве «оптимизации», вы включили только строку в заголовок ... Если вы обнаружите позже, вам не нужно больше строка в заголовке, и вы хотите удалить его (очистка кода и все ...), вам придется изменить источник, чтобы включить строку. Теперь давайте представим, что у вас есть 10 источников, включая заголовок ...

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

0 голосов
/ 27 апреля 2010

Считается плохой формой иметь оператор using в заголовочном файле, если только вы не намеренно дублируете символ в другом пространстве имен. Это нормально использовать в файле cpp.

Каждый typedef должен существовать только один раз в вашей кодовой базе. Это должно быть в заголовочном файле, если его нужно использовать в нескольких файлах cpp / h. Дублирование их принесет вам много горя.

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

0 голосов
/ 27 апреля 2010

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

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

0 голосов
/ 27 апреля 2010

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

...