Правило одного определения: различия между версиями

Материал из Википедии — свободной энциклопедии
Перейти к навигации Перейти к поиску
[непроверенная версия][отпатрулированная версия]
Содержимое удалено Содержимое добавлено
Нет описания правки
викификация
 
(не показано 17 промежуточных версий 15 участников)
Строка 1: Строка 1:
'''Правило одного определения (One Definition Rule, ODR)''' — один из основных принципов [[язык программирования|языка программирования]] [[C++]]. Назначение ODR состоит в том, чтобы в программе не могло появиться два или более конфликтующих между собой определения одной и той же сущности ([[Тип данных|типа данных]], [[Переменная (программирование)|переменной]], [[Функция (программирование)|функции]], [[Объект (программирование)|объекта]], [[Шаблоны C++|шаблона]]). Если это правило соблюдено, программа ведёт себя так, как будто в ней существует только одно, общее определение любой сущности. Нарушение ODR, если оно не будет обнаружено при [[Компиляция (программирование)|компиляции]] и [[Компоновщик|сборке]] проекта, приводит к [[Неопределённое поведение|непредсказуемому поведению программы]].
'''Правило одного определения (One Definition Rule)''' (ODR) одно из основных принципов [[язык программирования|языка программирования]] [[C++]].


== Причины появления ==
Вкратце, положения ODR выглядят так:
Синтаксис языка C++ определяет для ряда сущностей необходимость совмещения описания с объявлением, либо дублирование в объявлении описания. Это связано с отсутствием в языке полноценной поддержки [[Модульное программирование|модульности]], из-за чего на этапе компиляции файла в его тексте (то есть самом тексте компилируемого модуля и текстах всех включённых в него [[Заголовочный файл|заголовочных файлов]]) должны содержаться достаточные для компилятора сведения обо всех используемых программных сущностях, определённых в других [[Единица трансляции|единицах трансляции]]. Так, например, для компиляции файла, в котором используется некий класс, необходимо, по меньшей мере, определение самого класса и его публичных [[Свойство (программирование)|свойств]] и [[Метод (программирование)|методов]], а для использования в файле встраиваемой ([[Встраивание функций|inline]]) функции каждый использующий файл должен включать её полный текст. Ясно, что для обеспечения согласованности программы все такие определения в пределах системы должны быть идентичны. Обычно это обеспечивается тем, что [[Интерфейс (объектно-ориентированное программирование)|интерфейсная]] часть единицы трансляции описывается в соответствующем заголовочном файле, который затем подключается везде, где необходимо использовать определённые в ней сущности. Но в действительности ничто не мешает включить в разные компилируемые файлы разные описания одних и тех же элементов программы. Компилятор не сможет обнаружить такую ошибку, так как в момент трансляции он обрабатывает код только одного модуля.


Другая коллизия возможна в случае, когда в программе окажутся два различных определения одного и того же элемента, либо расхождение между внешним объявлением (со спецификатором extern) и фактическим определением того же элемента. Например, если у внешнего объявления функции и её определения окажется различное число [[Параметр (программирование)|параметров]], то вызов функции будет некорректным, но компилятор не сможет этого обнаружить, так как в момент работы видит только одну единицу трансляции.
# Любая [[единица трансляции]], [[шаблон]], [[тип данных]], [[Функция (программирование)|функция]], или [[объект]] не могут иметь более одного определения. Некоторые могут иметь неограниченное число объявлений. Определение порождает сущность.
# Во всей [[Компьютерная программа|программе]] объект или не[[вложенная функция]] не могут иметь больше одного определения; если объект или функция используются, у каждого из них должно быть строго по единственному опрделению. Можно объявить объект или функцию, которые не будут использованы, но в этом случае вы не должны создавать определение. Ни в коем случае не должно быть более одного определения.
# Некоторые вещи, например, типы, шаблоны или вложенные функции, могут определяться в более чем одной единице трансляции. Для данной сущности, каждое опредление должнобыть одним и тем же. Объекты или функции, не являющиеся внешними, в разных единицах трансляции имеют разные сущности, даже если их имена и типы совпадают.


В своей знаменитой книге «Введение в язык C++» [[Бьёрн Страуструп]] приводит следующий пример:
Некоторые нарушения ODR могут быть обнаружены [[компилятор]]ом. Остальные, а это в первую очередь касается программ, состоящих из нескольких небольших файлов, пропускаются (программист должен самостоятельно о них позаботиться).

В своей знаменитой книге «Введение в язык C++» [[Бьорн Страуструп]] приводит следующий пример:
// file1.c:
// file1.c:
int a = 1;
int a = 1;
Строка 20: Строка 17:
extern int c;
extern int c;


В данном примере 3 ошибки: определено дважды (int a; является определением, которое означает int a=0;), b описано дважды с разными типами, а c описано дважды, но не определено. Эти виды ошибок (ошибки компоновки) не могут быть обнаружены компилятором, который за один раз видит только один файл. Компоновщик, однако, их обнаруживает.
В данном примере 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++, соблюдение которых гарантирует адекватную обработку таких описаний и определений, сведены в описании языка в так называемое «Правило одного определения».

Положения правила

[править | править код]
  1. В пределах любой единицы трансляции шаблон, тип данных, функция, или объект не могут иметь более одного определения, хотя могут иметь неограниченное число объявлений. Определение порождает сущность.
  2. В пределах программы (совокупности всех единиц трансляции) внешний объект или внешняя не-inline функция не могут иметь больше одного определения; если объект или функция используются, у каждого из них должно быть строго по единственному определению. Можно объявить объект или функцию, которые не будут использованы в программе, в этом случае не потребуется и их определения. Ни в коем случае не должно быть более одного определения.
  3. Типы, шаблоны и inline-функции (то есть те сущности, у которых определение полностью или частично совмещается с объявлением) могут определяться в более чем одной единице трансляции, но для каждой такой сущности все её определения должны быть идентичны.
  4. Определения объектов и функций, не являющихся внешними, в разных единицах трансляции определяют различные сущности, даже если их имена и типы совпадают. Эти определения беспрепятственно могут различаться.

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

Дополнительная информация

[править | править код]