С ++ - объявление переменных класса в заголовке или .cpp?

До сих пор я использовал классы следующим образом:

GameEngine.h объявляет класс следующим образом

class GameEngine {
public:
    // Declaration of constructor and public methods

private:
    InputManager inputManager;
    int a, b, c;

    // Declaration of private methods
};

Мои файлы GameEngine.cpp тогда просто реализуют методы

#include "____.h"    
GameEngine::GameEngine() {

}

void GameEngine::run() {
    // stuff
}

Однако недавно я прочитал, что объявления переменных не должны находиться в файле заголовка. В приведенном выше примере это будет inputManager и a, b, c.

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

Однако я не уверен, имеет ли здесь смысл использование extern; Я просто объявляю частные переменные, которые будут использоваться только в экземпляре самого класса. В порядке ли мои объявления переменных в файлах заголовков? Или я должен положить их в другое место? Если я должен поместить их в файл cpp, они будут размещены непосредственно под #include?


person Kefir    schedule 14.02.2015    source источник
comment
объявления переменных не должны быть в файле заголовка .... что? Переменные-члены должны быть объявлены с объявлением класса.   -  person Cory Kramer    schedule 14.02.2015
comment
Если переменная предназначена для глобального использования, тогда extern переходит в заголовок, и вы определяете переменную один раз в каком-нибудь файле .cpp. Если это глобальные переменные, используемые для внутреннего использования, они попадают в .cpp. Если они логически являются частью класса, сделайте их переменными-членами.   -  person    schedule 14.02.2015
comment
Объявления переменных и объявления переменных member отличаются. Кроме того, проблемы возникают из-за определения переменных, не являющихся членами, а не из-за их объявления.   -  person Some programmer dude    schedule 14.02.2015
comment
Пытался, но больше не мог найти исходное заявление. На самом деле это было где-то на SO, но поскольку я пытался найти ответ, моя история слишком заполнена, чтобы найти его снова. Для меня это звучало как общее утверждение, что объявление переменных в файлах .h было плохим стилем программирования; Однако, учитывая вашу реакцию, я, вероятно, неправильно это понял.   -  person Kefir    schedule 14.02.2015


Ответы (3)


Не путайте члены типа с переменными. Определение класса / структуры просто описывает, что составляет тип, без фактического объявления существования каких-либо переменных, чего-либо, что должно быть построено в памяти, чего-либо адресуемого.

В традиционном смысле современные методы проектирования классов рекомендуют вам притвориться «черными ящиками»: все идет внутрь, они могут выполнять определенные задачи, возможно, выводить какую-то другую информацию. Мы постоянно делаем это с помощью методов класса, кратко описывая их сигнатуру в файле .h / .hpp / .hxx и скрывая детали реализации в файле .cpp / .cc / .cxx.

Хотя та же самая философия может быть применена к членам, текущее состояние C ++, как единицы перевода компилируются индивидуально, затрудняет реализацию этого способа. В этом нет ничего "нестандартного", что могло бы вам помочь. Основная, фундаментальная проблема заключается в том, что для почти любого использования вашего класса необходимо знать размер в байтах, и это что-то ограничивается полями-членами и порядком объявления. Даже если они частные и ничто за пределами типа не должно иметь возможности манипулировать ими, им все равно нужно вкратце знать, что они собой представляют.

Если вы действительно хотите скрыть эту информацию от посторонних, вам могут помочь определенные идиомы, такие как PImpl и встроенный PImpl. Но я бы порекомендовал вам не идти этим путем, если вы на самом деле:

  1. Написание библиотеки с полустабильным ABI, даже если вы внесете массу изменений.
  2. Необходимо скрыть непереносимый, зависящий от платформы код.
  3. Необходимо сократить время препроцессора из-за обилия включений.
  4. Необходимо сократить время компиляции, на которое напрямую влияет раскрытие информации.

На самом деле в руководстве говорится о том, чтобы никогда не объявлять глобальные переменные в заголовках. Любая единица перевода, которая использует ваш заголовок, даже если косвенно, в конечном итоге объявит свою собственную глобальную переменную в соответствии с инструкциями заголовка. При индивидуальном рассмотрении все будет скомпилировано нормально, но компоновщик будет жаловаться на то, что у вас есть несколько определений для одного и того же (что является большим запретом в C ++).

Если вам нужно зарезервировать память / построить что-то и привязать это к имени переменной, всегда старайтесь сделать это в исходном файле (файлах).

person ZaldronGG    schedule 14.02.2015
comment
У меня есть дополнительный вопрос: это строка Inputmanager inputManager; просто декларация, или она уже попытается зарезервировать память? Изначально я хотел инициализировать inputManager в конструкторе GameEngine, однако он не компилировался, потому что для InputManager не было конструктора по умолчанию. (Конструктора по умолчанию не было, но я тоже нигде не вызывал конструктор без аргументов.) Это меня удивило, потому что я просто хотел объявить о существовании переменной, но казалось, что ему нужен конструктор по умолчанию прямо в объявлении. . - person Kefir; 14.02.2015
comment
В том виде, в каком он стоит в объявлении вашего класса, вы заявляете, что он полностью содержит InputManager, но до сих пор фактически ничего не было построено. Как только вы создадите объект GameEngine в среде выполнения с помощью конструктора, у него будет секция байтов, полностью посвященная хранению в нем всего InputManager. Конструктор также попытается создать InputManager. (если вы явно не объясняете, как его создать, и конструктор по умолчанию не существует, само собой разумеется, что он будет жаловаться на это во время компиляции) «Право собственности» в C ++ - это слишком широкая тема для комментария. - person ZaldronGG; 14.02.2015

Переменные-члены класса должны быть объявлены в определении класса, которое обычно находится в файле заголовка. Это должно быть сделано без каких-либо extern ключевых слов, совершенно нормально, как вы это делали до сих пор.

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

person emlai    schedule 14.02.2015

Как общее правило:

Переменные, которые должны использоваться с множеством функций в одном классе, указываются в объявлении класса.

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

Кажется, что InputManager inputManager; принадлежит заголовку класса.

int a, b, c; отсюда понять труднее. Для чего они нужны? Они выглядят как временные переменные, которые лучше использовать в функциях, в которых они используются, но я не могу сказать наверняка без надлежащего контекста.

extern здесь не нужен.

person Michael Gazonda    schedule 14.02.2015