C++
Одно правило определения (ODR)
Поиск…
Умножённая функция
Важнейшим следствием правила Единого определения является то, что не-встроенные функции с внешней связью следует определять только один раз в программе, хотя они могут быть объявлены несколько раз. Поэтому такие функции не должны определяться в заголовках, поскольку заголовок может быть включен несколько раз из разных единиц перевода.
foo.h :
#ifndef FOO_H
#define FOO_H
#include <iostream>
void foo() { std::cout << "foo"; }
void bar();
#endif
foo.cpp :
#include "foo.h"
void bar() { std:: cout << "bar"; }
main.cpp :
#include "foo.h"
int main() {
foo();
bar();
}
В этой программе функция foo определена в заголовке foo.h , который включается дважды: один раз из foo.cpp и один раз из main.cpp . Поэтому каждая единица перевода содержит свое собственное определение foo . Обратите внимание, что включение foo.h в foo.h не мешает этому, поскольку foo.cpp и main.cpp оба отдельно включают foo.h Наиболее вероятным результатом попытки создания этой программы является ошибка времени соединения, определяющая foo как многократно определенную.
Чтобы избежать таких ошибок, следует объявлять функции в заголовках и определять их в соответствующих файлах .cpp , за некоторыми исключениями (см. Другие примеры).
Встроенные функции
Функция, объявленная в inline может быть определена в нескольких единицах перевода, при условии, что все определения идентичны. Он также должен быть определен в каждой единицы перевода, в которой он используется. Поэтому встроенные функции должны быть определены в заголовках, и нет необходимости упоминать их в файле реализации.
Программа будет вести себя так, как если бы было одно определение функции.
foo.h :
#ifndef FOO_H
#define FOO_H
#include <iostream>
inline void foo() { std::cout << "foo"; }
void bar();
#endif
foo.cpp :
#include "foo.h"
void bar() {
// more complicated definition
}
main.cpp :
#include "foo.h"
int main() {
foo();
bar();
}
В этом примере более простая функция foo определена внутри строки в файле заголовка, тогда как более сложная функциональная bar не является встроенной и определена в файле реализации. Оба foo.cpp перевода foo.cpp и main.cpp содержат определения foo , но эта программа хорошо сформирована, поскольку foo является встроенным.
Функция, определенная в определении класса (которая может быть функцией-членом или функцией друга), неявно встроена. Поэтому, если класс определен в заголовке, функции-члены класса могут быть определены в определении класса, хотя определения могут быть включены в несколько единиц перевода:
// in foo.h
class Foo {
void bar() { std::cout << "bar"; }
void baz();
};
// in foo.cpp
void Foo::baz() {
// definition
}
Функция Foo::baz определяется вне строки, поэтому она не является встроенной функцией и не должна определяться в заголовке.
Нарушение ODR с помощью разрешения перегрузки
Даже при идентичных токенах для встроенных функций ODR может быть нарушен, если поиск имен не относится к одному и тому же объекту. рассмотрим func в следующем:
header.h
void overloaded(int); inline void func() { overloaded('*'); }foo.cpp
#include "header.h" void foo() { func(); // `overloaded` refers to `void overloaded(int)` }bar.cpp
void overloaded(char); // can come from other include #include "header.h" void bar() { func(); // `overloaded` refers to `void overloaded(char)` }
У нас есть нарушение ODR, поскольку overloaded относится к различным объектам в зависимости от единицы перевода.