C++
Eine Definitionsregel (ODR)
Suche…
Mehrfach definierte Funktion
Die wichtigste Konsequenz der One-Definition-Regel ist, dass Nicht-Inline-Funktionen mit externer Verknüpfung nur einmal in einem Programm definiert werden sollten, obwohl sie mehrmals deklariert werden können. Daher sollten solche Funktionen nicht in Headern definiert werden, da ein Header aus verschiedenen Übersetzungseinheiten mehrfach eingefügt werden kann.
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();
}
In diesem Programm ist die Funktion foo
im Header foo.h
, der zweimal enthalten ist: einmal aus foo.cpp
und einmal aus main.cpp
. Jede Übersetzungseinheit enthält daher ihre eigene Definition von foo
. Beachten Sie, dass die Include-Guards in foo.h
dies nicht verhindern, da foo.cpp
und main.cpp
beide foo.h
separat enthalten. Das wahrscheinlichste Ergebnis des Versuchs, dieses Programm zu erstellen, ist ein Verbindungszeitfehler, der foo
als mehrfach definiert identifiziert.
Um solche Fehler zu vermeiden, sollte man Funktionen in Header deklariert und definiert sie in den entsprechenden .cpp
- Dateien, mit einigen Ausnahmen (andere siehe Beispiele).
Inline-Funktionen
Eine inline
deklarierte Funktion kann in mehreren Übersetzungseinheiten definiert werden, vorausgesetzt, dass alle Definitionen identisch sind. Es muss auch in jeder Übersetzungseinheit definiert werden, in der es verwendet wird. Inline-Funktionen sollten daher in Kopfzeilen definiert werden und müssen nicht in der Implementierungsdatei erwähnt werden.
Das Programm verhält sich so, als ob es eine einzige Definition der Funktion gibt.
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();
}
In diesem Beispiel ist die einfachere Funktion foo
wird inline in der Header - Datei definiert , während das kompliziertere Funktion bar
nicht inline ist und in der Implementierungsdatei definiert. Sowohl die foo.cpp
und main.cpp
Übersetzungseinheiten enthalten Definitionen von foo
, aber dieses Programm ist gut gebildet , da foo
inline ist.
Eine in einer Klassendefinition definierte Funktion (die eine Member- oder eine Friend-Funktion sein kann) ist implizit inline. Wenn also eine Klasse in einem Header definiert ist, können Member-Funktionen der Klasse innerhalb der Klassendefinition definiert werden, auch wenn die Definitionen in mehreren Übersetzungseinheiten enthalten sind:
// in foo.h
class Foo {
void bar() { std::cout << "bar"; }
void baz();
};
// in foo.cpp
void Foo::baz() {
// definition
}
Die Funktion Foo::baz
ist als Out-of-Line definiert, es handelt sich also nicht um eine Inline-Funktion und darf nicht im Header definiert werden.
ODR-Verletzung durch Überlastlösung
Selbst bei identischen Token für Inline-Funktionen kann ODR verletzt werden, wenn das Nachschlagen von Namen nicht auf dieselbe Entität verweist. Betrachten wir func
im Folgenden:
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)` }
Wir haben eine ODR-Verletzung, da overloaded
abhängig von der Übersetzungseinheit auf verschiedene Entitäten verweist.