C++
Reguła jednej definicji (ODR)
Szukaj…
Wielokrotnie zdefiniowana funkcja
Najważniejszą konsekwencją reguły One Definition jest to, że funkcje nieliniowe z zewnętrznym łączeniem powinny być definiowane tylko raz w programie, chociaż mogą być deklarowane wielokrotnie. Dlatego takich funkcji nie należy definiować w nagłówkach, ponieważ nagłówek może być dołączany wiele razy z różnych jednostek tłumaczeniowych.
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();
}
W tym programie funkcja foo
jest zdefiniowana w nagłówku foo.h
, który jest zawarty dwukrotnie: raz z foo.cpp
i raz z main.cpp
. Każda jednostka tłumacząca zawiera zatem własną definicję foo
. Zauważ, że dołączenia strażników w foo.h
nie zapobiegają temu, ponieważ zarówno foo.cpp
i main.cpp
zawierają osobno foo.h
Najbardziej prawdopodobnym rezultatem próby zbudowania tego programu jest błąd czasu połączenia identyfikujący foo
jako wielokrotnie zdefiniowany.
Aby uniknąć takich błędów, należy zadeklarować funkcje w nagłówkach i zdefiniować je w odpowiednich plikach .cpp
, z pewnymi wyjątkami (patrz inne przykłady).
Funkcje wbudowane
Funkcja zadeklarowana jako inline
może być zdefiniowana w wielu jednostkach tłumaczeniowych, pod warunkiem, że wszystkie definicje są identyczne. Musi być także zdefiniowany w każdej jednostce tłumaczeniowej, w której jest używany. Dlatego funkcje wbudowane powinny być zdefiniowane w nagłówkach i nie trzeba ich wymieniać w pliku implementacji.
Program będzie się zachowywał tak, jakby istniała jedna definicja funkcji.
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();
}
W tym przykładzie prostsza funkcja foo
jest zdefiniowana w pliku nagłówkowym, podczas gdy bardziej skomplikowany bar
funkcji nie jest wbudowany i jest zdefiniowany w pliku implementacji. Zarówno jednostki tłumaczące foo.cpp
i main.cpp
zawierają definicje foo
, ale ten program jest dobrze sformułowany, ponieważ foo
jest wbudowany.
Funkcja zdefiniowana w definicji klasy (która może być funkcją składową lub funkcją zaprzyjaźnioną) jest domyślnie wbudowana. Dlatego jeśli klasa jest zdefiniowana w nagłówku, funkcje składowe klasy mogą być zdefiniowane w ramach definicji klasy, nawet jeśli definicje mogą być zawarte w wielu jednostkach tłumaczeniowych:
// in foo.h
class Foo {
void bar() { std::cout << "bar"; }
void baz();
};
// in foo.cpp
void Foo::baz() {
// definition
}
Funkcja Foo::baz
definiuje out-of-line, więc nie jest to funkcja inline, a nie muszą być zdefiniowane w nagłówku.
Naruszenie ODR przez rozwiązanie przeciążenia
Nawet przy identycznych tokenach dla funkcji wbudowanych ODR może zostać naruszone, jeśli wyszukiwanie nazw nie odnosi się do tej samej jednostki. rozważmy func
w następujący sposób:
nagłówek. 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)` }
Mamy naruszenie ODR, ponieważ overloaded
odnosi się do różnych podmiotów w zależności od jednostki tłumaczeniowej.