Где разместить заголовки моей стандартной библиотеки при создании классов в C ++? - PullRequest
0 голосов
/ 07 октября 2018

Поправьте меня, если я ошибаюсь, то, что #ifndef в начале заголовков C ++ специально помогает избежать повторения кода?

Итак (в соответствии с передовой практикой), куда я помещаю базовые библиотечные заголовки, такие как или ?

Позвольте мне прояснить вопрос на примере

Допустим, у меня есть программа, которая создает классы Employee и хранит информацию о них, например, их имена или что-то в этом роде

  1. У меня есть int main (), который использует std :: cout ALOT для отображения информации на консоли.Он может даже использовать жестко закодированные строковые переменные для хранения информации в одном из классов (я знаю, что жесткое кодирование плохо, просто потерпите меня, это поможет мне разобраться)

  2. У меня естьзаголовок с именем Employee.h со всеми моими личными переменными ... в данном случае это имя сотрудника ... и он также имеет объявления функций.Предположим, что он использует строковый тип данных в одном из конструкторов.

  3. Employee.cpp, который устанавливает и получает ... круто. Также давайте притворимся, что у него есть какая-то странная функция, которая манипулируетИмя сотрудника (которое является строкой) и добавьте перед ним как "yolo".Так что этот файл также потребует доступа к библиотеке

Пока: все три моих файла хотят использовать заголовок и только main.cppхочет использовать заголовок .

Мой вопрос (ы):

Похоже, заголовок можно поместить в заголовок Eployee, а не в main.cppи программа скомпилируется и будет работать нормально ... но если я добавлю ее в главный, а не в заголовок Employee, компилятор сойдет с ума.Так стоит ли включать его как в основной, так и в заголовок, или только в заголовок?

Изменится ли этот передовой опыт, если я создам второй класс под названием Companies, который также использует библиотеку строк?

PS может кто-то, пожалуйста, объясните, как обычные препроцессоры связывают вещи .. например, почему компьютер выдает ошибки, когда включается только в main, но не в заголовок ... Я думал, что все это было связано вместе...Im тупой, пожалуйста, объясните

(я думаю, что я понимаю, что все mumbo jumbo о файлах obj, но тогда как препроцессоры и компиляторы знают, что код заголовка не повторяется как в классе .cpp, так и в основном .cpp?

Спасибо за терпение, если вы прочитали это, я также признателен за любую помощь, направленную таким образом к нубу с ++, как я.

1 Ответ

0 голосов
/ 07 октября 2018

Согласно стандарту C ++:

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

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

AНебольшой пример того, как генерируется единица перевода: Изначально у нас есть заголовок.Давайте назовем это stdfoo.h (это пример, вам следует избегать именования собственных заголовков с помощью std, чтобы избежать коллизий.)

#ifndef STD_FOO_ // here is our header guard
#define STD_FOO_

typedef long howl_t;
void foo();

#endif // STD_FOO_ close the header guard at the end

Давайте включим это в исходный код нашего проекта main.cxx:

#include <stdfoo.h>

int main {
    foo();
    return 0;
};

Когда мы компилируем это, он запускается через препроцессор в модуль перевода.Давайте посмотрим, как может выглядеть этот сгенерированный модуль:

typedef long howl_t;
void foo();

int main {
    foo();
    return 0;
};

Директива #include расширила stdfoo.h в модуле перевода, так что компилятор может посмотреть на один модуль перевода и сгенерировать объект.

Давайте изменим все и дадим main.cxx заголовок, main.h.

#ifndef MAIN_H
#define MAIN_H

class BarkBark {
    BarkBark() {}
    void emit();
};

#endif // MAIN_H

Используйте его в нашем новом main.cxx:

#include "main.h"
#include <stdfoo.h>

int main {
    BarkBark woof;
    woof.emit();
    foo();
    return 0;
};

Единица перевода выглядела бы следующим образом:

class BarkBark {
    BarkBark() {}
    void emit();
};

typedef long howl_t;
void foo();

int main {
    BarkBark woof;
    woof.emit();
    foo();
    return 0;
};

Теперь, скажем, emit использует howl_t в качестве аргумента, например void emit(howl_t h), для этого потребуется либо переопределить howl_t, рискованный способ, либо включить stdfoo.h in main.h

#ifndef MAIN_H
#define MAIN_H

#include <stdfoo.h>

class BarkBark {
    BarkBark() {}
    void emit(howl_t h);
};

#endif // MAIN_H

Как выглядит этот модуль перевода?

typedef long howl_t;
void foo();

class BarkBark {
    BarkBark() {}
    void emit();
};

int main {
    howl_t aroooooo = 0;
    BarkBark woof;
    woof.emit(aroooooo);
    return 0;
};

Препроцессор расширил #include внутри заголовка #include d.

Обратите внимание, останется ли #include <stdfoo.h> в main.cxx, будет ли блок перевода выглядеть одинаково из-за того, что препроцессор обрабатывает защиту заголовков, второе включение просто отбрасывается.

Теперь, насколько это стандартноЗаголовки библиотек, большинство не всех стандартных библиотек защищаютполучить множественное включение, так что вы можете #include тогда так часто и так часто, как пожелает ваше сердце без вреда для здоровья.Полученный модуль перевода включит его только один раз для компиляции.Обратите внимание, что это не гарантируется с любыми другими заголовками, и многократное включение неохраняемых заголовков может привести к очень неясным ошибкам, поскольку дубликаты объявлений существуют только в модуле перевода.определенный класс или функция, которая будет включена, #include это в заголовке.На этом этапе это стилистический выбор, решите ли вы #include также в исходном файле.Некоторые говорят, что делать в случае изменения заголовка, некоторые говорят, не упрощать код.Этот выбор действительно остается за вами.

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

Однако!Это не жесткое и быстрое правило.Некоторые делают это по-разному и располагают #include в логических позициях, где они могут использоваться или где определенные заголовки используются в нескольких исходных файлах, чтобы уменьшить дублирование кода.Лично я думаю, что эта договоренность добавила технического долга, если код может быть улучшен или подвергнут рефакторингу, и заголовки станут осиротевшими.

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