Зачем включать защитные устройства, которые не мешают описанию нескольких функций?

Компонент сообщает об этом дублирующем символе:

#ifndef testttt
#define testttt

void anything(){
    std::cout<<"hellooooooo";
}

#endif

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

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

Итак, я думаю, что мой вопрос из двух частей: 1) Почему включенные охранники не предотвращают множественные определения этой функции, как они делают для других элементов заголовка, и 2) Почему слово static разрешает это, когда static Предполагалось, чтобы имена не были видимыми в других единицах перевода? Я добавляю его, и на самом деле я могу вызвать эту функцию из любого места, включая этот заголовочный файл.

Ответ 1

"1) Почему включенные защитные устройства не предотвращают множественные определения этой функции, как они делают для других элементов заголовка"

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

"2) Почему статическое слово разрешает это, когда статический объект должен предотвращать видимость имен в других единицах перевода?"

Поскольку ключевое слово static делает частную копию этой функции для каждой единицы перевода.

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

Ответ 2

Почему включенные охранники не предотвращают несколько определений этой функции, как они делают для других элементов заголовка?

Процесс создания исполняемого файла из программы на С++ состоит из трех этапов:

  • Предварительная обработка
  • Компиляция &
  • Связь

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

Защитники заголовка предотвращают включение содержимого заголовка несколько раз в то же самое блок перевода во время Предварительная обработка. Они не препятствуют включению содержимого в разные единицы перевода. Когда вы включаете этот файл заголовка в разные единицы перевода, каждый из этих блоков будет иметь определение этой функции.
Компилятор компилирует каждую единицу перевода отдельно для создания отдельного объектного файла (.o), каждый из этих файлов .o будет иметь копию этого определения функции. Когда компоновщик пытается связать с определением функции во время генерации .exe, он находит несколько определений одних и тех же функций, тем самым вызывая путаницу в отношении того, к какой привязке. Чтобы избежать этой проблемы, стандарт определяет правило, известное как одно правило дефиниции (ODR), которое запрещает несколько определений того же объекта.
Как вы видите, включая определение функции в файле заголовка и включение этого файла заголовка в несколько единиц перевода, нарушает ODR.
Обычный способ сделать это - предоставить объявление в файле заголовка и определение в одном и только одном исходном файле.

Почему статическое слово разрешает это, когда статический объект должен предотвращать видимость имен в других единицах перевода?

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


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

  • Отметьте функцию как inline или
  • Отметьте функцию как static или
  • Поместите функцию в неназванное пространство имен.

Обратите внимание, что #1 и #2 делают то же самое, что указано во втором ответе выше.
С помощью #3 стандарт расслабляет ODR для встроенных функций и позволяет каждому модулю перевода иметь собственное определение (при условии, что все определения одинаковы).

Итак, если вы действительно хотите поместить определение функции в заголовок #1, это правильный способ сделать это.

Ответ 3

1) Почему включенные защитные устройства не предотвращают множественные определения этой функции, как они делают для других элементов заголовка,

Включить защиту от множественного включения заголовка в блок тот же. Однако он не предотвращает несколько определений: если заголовок включен в несколько единиц перевода, тогда будет много ошибок определения, потому что функция определена в каждой единицы перевода, и поскольку она имеет внешний, все единицы перевода могут видеть определение из всех других единиц перевода. Чтобы предотвратить эту ошибку, вам нужно предоставить объявление в заголовке и предоставить определение в файле ONE .cpp.

Читайте о Одно правило определения (ODR) и Внешнее взаимодействие.

2) Почему статическое слово разрешает это, когда статический объект должен предотвращать видимость имен в других единицах перевода?

Потому что static делает внутреннюю функцию для каждой единицы перевода. Это означает, что связь внутренняя означает: другая единица перевода не может видеть определение.