Во-первых, вы не должны помещать в заголовки ничего, что не нужно было бы видеть другим файлом, кроме того, который ему нужен. Затем давайте определим что-то, что нам нужно ниже.
Блок перевода
Единица перевода - это текущий код, который компилируется, и весь код включен
прямо или косвенно. Один переводчик переводит в один файл .o / .obj.
Программа
Это все ваши файлы .o / .obj, связанные вместе в один двоичный файл, который может быть
выполняется для формирования процесса.
Каковы основные моменты наличия различных единиц перевода?
- Уменьшите зависимости, чтобы при изменении одного метода одного класса вам не приходилось перекомпилировать весь код вашей программы, а только уязвимую единицу перевода.
- Уменьшите возможные конфликты имен, используя локальные имена единиц перевода, которые не видны другим единицам перевода при их объединении.
Теперь, как вы можете разбить свой код на разные единицы перевода? Ответ таков: «Так что делай!», Но ты должен учитывать это в каждом конкретном случае. Это часто понятно, поскольку у вас есть разные классы, которые можно и нужно помещать в разные единицы перевода:
foo.hpp:
/* Only declaration of class foo we define below. Note that a declaration
* is not a definition. But a definition is always also a declaration */
class foo;
/* definition of a class foo. the same class definition can appear
in multiple translation units provided that each definition is the same
basicially, but only once per translation unit. This too is called the
"One Definition Rule" (ODR). */
class foo {
/* declaration of a member function doit */
void doit();
/* definition of an data-member age */
int age;
};
Объявление некоторых свободных функций и объектов:
/* if you have translation unit non-local (with so-called extern linkage)
names, you declare them here, so other translation units can include
your file "foo.hpp" and use them. */
void getTheAnswer();
/* to avoid that the following is a definition of a object, you put "extern"
in front of it. */
extern int answerCheat;
foo.cpp:
/* include the header of it */
#include "foo.hpp"
/* definition of the member function doit */
void foo::doit() {
/* ... */
}
/* definition of a translation unit local name. preferred way in c++. */
namespace {
void help() {
/* ... */
}
}
void getTheAnswer() {
/* let's call our helper function */
help();
/* ... */
}
/* define answerCheat. non-const objects are translation unit nonlocal
by default */
int answerCheat = 42;
bar.hpp:
/* so, this is the same as above, just with other classes/files... */
class bar {
public:
bar(); /* constructor */
};
bar.cpp:
/* we need the foo.hpp file, which declares getTheAnswer() */
#include "foo.hpp"
#include "bar.hpp"
bar::bar() {
/* make use of getTheAnswer() */
getTheAnswer();
}
Обратите внимание, что имена в анонимном пространстве имен (как указано выше) не конфликтуют, так как они кажутся локальными единицами перевода. на самом деле это не так, у них просто уникальные имена, чтобы они не конфликтовали. если вы действительно хотите (есть небольшая причина) переводить локальные имена модулей (например, из-за совместимости с c, чтобы код C мог вызывать вашу функцию), вы можете сделать это следующим образом:
static void help() {
/* .... */
}
В ODR также сказано, что в одной программе нельзя иметь более одного определения какого-либо объекта или не встроенной функции (классы - это типы, а не объекты, поэтому к ним это не относится). Таким образом, вы должны остерегаться не помещать не встроенные функции в заголовки или не помещать объекты типа "int foo;" в заголовках. Это приведет к ошибкам компоновщика тогда, когда компоновщик попытается связать блоки перевода, включая эти заголовки вместе.
Я надеюсь, что смогу вам немного помочь. Теперь это был длинный ответ, где-то действительно есть ошибки. Я знаю, что единица перевода строго определяется другим способом (вывод препроцессора). Но я думаю, что это не добавит большого значения, чтобы включить это в вышесказанное, и это запутает вопрос. Пожалуйста, не стесняйтесь бить меня, если вы найдете настоящие ошибки:)