Правило одного определения: различия между версиями
[непроверенная версия] | [отпатрулированная версия] |
Нет описания правки |
викификация |
||
(не показано 17 промежуточных версий 15 участников) | |||
Строка 1: | Строка 1: | ||
'''Правило одного определения (One Definition Rule, ODR)''' — один из основных принципов [[язык программирования|языка программирования]] [[C++]]. Назначение ODR состоит в том, чтобы в программе не могло появиться два или более конфликтующих между собой определения одной и той же сущности ([[Тип данных|типа данных]], [[Переменная (программирование)|переменной]], [[Функция (программирование)|функции]], [[Объект (программирование)|объекта]], [[Шаблоны C++|шаблона]]). Если это правило соблюдено, программа ведёт себя так, как будто в ней существует только одно, общее определение любой сущности. Нарушение ODR, если оно не будет обнаружено при [[Компиляция (программирование)|компиляции]] и [[Компоновщик|сборке]] проекта, приводит к [[Неопределённое поведение|непредсказуемому поведению программы]]. |
|||
'''Правило одного определения (One Definition Rule)''' (ODR) одно из основных принципов [[язык программирования|языка программирования]] [[C++]]. |
|||
== Причины появления == |
|||
Вкратце, положения ODR выглядят так: |
|||
Синтаксис языка C++ определяет для ряда сущностей необходимость совмещения описания с объявлением, либо дублирование в объявлении описания. Это связано с отсутствием в языке полноценной поддержки [[Модульное программирование|модульности]], из-за чего на этапе компиляции файла в его тексте (то есть самом тексте компилируемого модуля и текстах всех включённых в него [[Заголовочный файл|заголовочных файлов]]) должны содержаться достаточные для компилятора сведения обо всех используемых программных сущностях, определённых в других [[Единица трансляции|единицах трансляции]]. Так, например, для компиляции файла, в котором используется некий класс, необходимо, по меньшей мере, определение самого класса и его публичных [[Свойство (программирование)|свойств]] и [[Метод (программирование)|методов]], а для использования в файле встраиваемой ([[Встраивание функций|inline]]) функции каждый использующий файл должен включать её полный текст. Ясно, что для обеспечения согласованности программы все такие определения в пределах системы должны быть идентичны. Обычно это обеспечивается тем, что [[Интерфейс (объектно-ориентированное программирование)|интерфейсная]] часть единицы трансляции описывается в соответствующем заголовочном файле, который затем подключается везде, где необходимо использовать определённые в ней сущности. Но в действительности ничто не мешает включить в разные компилируемые файлы разные описания одних и тех же элементов программы. Компилятор не сможет обнаружить такую ошибку, так как в момент трансляции он обрабатывает код только одного модуля. |
|||
Другая коллизия возможна в случае, когда в программе окажутся два различных определения одного и того же элемента, либо расхождение между внешним объявлением (со спецификатором extern) и фактическим определением того же элемента. Например, если у внешнего объявления функции и её определения окажется различное число [[Параметр (программирование)|параметров]], то вызов функции будет некорректным, но компилятор не сможет этого обнаружить, так как в момент работы видит только одну единицу трансляции. |
|||
⚫ | |||
⚫ | # |
||
# Некоторые вещи, например, типы, шаблоны или вложенные функции, могут определяться в более чем одной единице трансляции. Для данной сущности, каждое опредление должнобыть одним и тем же. Объекты или функции, не являющиеся внешними, в разных единицах трансляции имеют разные сущности, даже если их имена и типы совпадают. |
|||
⚫ | |||
Некоторые нарушения ODR могут быть обнаружены [[компилятор]]ом. Остальные, а это в первую очередь касается программ, состоящих из нескольких небольших файлов, пропускаются (программист должен самостоятельно о них позаботиться). |
|||
⚫ | |||
// file1.c: |
// file1.c: |
||
int a = 1; |
int a = 1; |
||
Строка 20: | Строка 17: | ||
extern int c; |
extern int c; |
||
В данном примере 3 ошибки: определено дважды (int a; является определением, |
В данном примере 3 ошибки: '''a''' определено дважды («int a;» является определением, где начальное значение a не задано), '''b''' в описании и определении имеет различные типы, а '''c''' описано дважды, но не определено. Эти виды ошибок в C++ не могут быть обнаружены [[компилятор]]ом. В большинстве случаев их обнаруживает [[компоновщик]]. |
||
Ограничения, накладываемые на повторные описания и определения программных сущностей в C++, соблюдение которых гарантирует адекватную обработку таких описаний и определений, сведены в описании языка в так называемое «Правило одного определения». |
|||
== Положения правила == |
|||
⚫ | # В пределах любой [[единица трансляции|единицы трансляции]] [[Шаблоны C++|шаблон]], [[тип данных]], [[Функция (программирование)|функция]], или [[Объект (программирование)|объект]] не могут иметь более одного определения, хотя могут иметь неограниченное число объявлений. Определение порождает сущность. |
||
⚫ | # В пределах [[Компьютерная программа|программы]] (совокупности всех единиц трансляции) внешний объект или внешняя не-inline [[Функция (программирование)|функция]] не могут иметь больше одного определения; если объект или функция используются, у каждого из них должно быть строго по единственному определению. Можно объявить объект или функцию, которые не будут использованы в программе, в этом случае не потребуется и их определения. Ни в коем случае не должно быть более одного определения. |
||
# Типы, шаблоны и [[Встраивание функций|inline-функции]] (то есть те сущности, у которых определение полностью или частично совмещается с объявлением) могут определяться в более чем одной единице трансляции, но для каждой такой сущности все её определения должны быть идентичны. |
|||
# Определения объектов и функций, не являющихся внешними, в разных единицах трансляции определяют различные сущности, даже если их имена и типы совпадают. Эти определения беспрепятственно могут различаться. |
|||
Некоторые нарушения ODR могут быть найдены [[компилятор]]ом, но большинство из них обнаруживаются только на этапе компоновки. |
|||
== Дополнительная информация == |
== Дополнительная информация == |
||
* [[Заголовочный файл]] |
* [[Заголовочный файл]] |
||
[[Категория: |
[[Категория:Синтаксис C++]] |
||
[[en:One_Definition_Rule]] |
|||
{{изолированная статья}} |
|||
{{rq|wikify|sources}} |
{{rq|wikify|sources}} |
Текущая версия от 08:46, 25 января 2021
Правило одного определения (One Definition Rule, ODR) — один из основных принципов языка программирования C++. Назначение ODR состоит в том, чтобы в программе не могло появиться два или более конфликтующих между собой определения одной и той же сущности (типа данных, переменной, функции, объекта, шаблона). Если это правило соблюдено, программа ведёт себя так, как будто в ней существует только одно, общее определение любой сущности. Нарушение ODR, если оно не будет обнаружено при компиляции и сборке проекта, приводит к непредсказуемому поведению программы.
Причины появления
[править | править код]Синтаксис языка C++ определяет для ряда сущностей необходимость совмещения описания с объявлением, либо дублирование в объявлении описания. Это связано с отсутствием в языке полноценной поддержки модульности, из-за чего на этапе компиляции файла в его тексте (то есть самом тексте компилируемого модуля и текстах всех включённых в него заголовочных файлов) должны содержаться достаточные для компилятора сведения обо всех используемых программных сущностях, определённых в других единицах трансляции. Так, например, для компиляции файла, в котором используется некий класс, необходимо, по меньшей мере, определение самого класса и его публичных свойств и методов, а для использования в файле встраиваемой (inline) функции каждый использующий файл должен включать её полный текст. Ясно, что для обеспечения согласованности программы все такие определения в пределах системы должны быть идентичны. Обычно это обеспечивается тем, что интерфейсная часть единицы трансляции описывается в соответствующем заголовочном файле, который затем подключается везде, где необходимо использовать определённые в ней сущности. Но в действительности ничто не мешает включить в разные компилируемые файлы разные описания одних и тех же элементов программы. Компилятор не сможет обнаружить такую ошибку, так как в момент трансляции он обрабатывает код только одного модуля.
Другая коллизия возможна в случае, когда в программе окажутся два различных определения одного и того же элемента, либо расхождение между внешним объявлением (со спецификатором extern) и фактическим определением того же элемента. Например, если у внешнего объявления функции и её определения окажется различное число параметров, то вызов функции будет некорректным, но компилятор не сможет этого обнаружить, так как в момент работы видит только одну единицу трансляции.
В своей знаменитой книге «Введение в язык C++» Бьёрн Страуструп приводит следующий пример:
// file1.c: int a = 1; int b = 1; extern int c;
// file2.c: int a; extern double b; extern int c;
В данном примере 3 ошибки: a определено дважды («int a;» является определением, где начальное значение a не задано), b в описании и определении имеет различные типы, а c описано дважды, но не определено. Эти виды ошибок в C++ не могут быть обнаружены компилятором. В большинстве случаев их обнаруживает компоновщик.
Ограничения, накладываемые на повторные описания и определения программных сущностей в C++, соблюдение которых гарантирует адекватную обработку таких описаний и определений, сведены в описании языка в так называемое «Правило одного определения».
Положения правила
[править | править код]- В пределах любой единицы трансляции шаблон, тип данных, функция, или объект не могут иметь более одного определения, хотя могут иметь неограниченное число объявлений. Определение порождает сущность.
- В пределах программы (совокупности всех единиц трансляции) внешний объект или внешняя не-inline функция не могут иметь больше одного определения; если объект или функция используются, у каждого из них должно быть строго по единственному определению. Можно объявить объект или функцию, которые не будут использованы в программе, в этом случае не потребуется и их определения. Ни в коем случае не должно быть более одного определения.
- Типы, шаблоны и inline-функции (то есть те сущности, у которых определение полностью или частично совмещается с объявлением) могут определяться в более чем одной единице трансляции, но для каждой такой сущности все её определения должны быть идентичны.
- Определения объектов и функций, не являющихся внешними, в разных единицах трансляции определяют различные сущности, даже если их имена и типы совпадают. Эти определения беспрепятственно могут различаться.
Некоторые нарушения ODR могут быть найдены компилятором, но большинство из них обнаруживаются только на этапе компоновки.
Дополнительная информация
[править | править код]Для улучшения этой статьи желательно:
|