ProgrammierungSenior C++ Entwickler

Erzählen Sie von dem Problem „One Definition Rule (ODR)“ in C++. Wie wirkt es sich auf große Projekte aus und wie kann man Verstöße vermeiden?

Bestehen Sie Vorstellungsgespräche mit dem Hintsage-KI-Assistenten

Antwort

One Definition Rule (ODR) ist eine grundlegende Regel in C++, die vorschreibt, dass es im gesamten Programm (allen Übersetzungseinheiten) für jedes Objekt, jede Funktion oder jede Klasse genau eine definierte Implementierung geben muss.

ODR wird verletzt, wenn:

  • Mehrere Implementierungen derselben Funktion/Klasse mit unterschiedlichem Code in verschiedenen .cpp-Dateien erscheinen.
  • Eine Inline-Funktion oder Vorlage unterschiedliche Implementierungen an verschiedenen Einbindepunkten hat.

Dies führt zu schwer zu erkennenden, unvorhersehbaren Bugs, inkorrekter Linkerarbeit oder, noch schlimmer, zu unterschiedlichen Verhaltensweisen des Programms, abhängig davon, wie es zusammengestellt wurde.

Warum kommt es zu Verstößen:

In großen Projekten werden oft .h-Dateien ohne Versionskontrolle kopiert/modifiziert oder der Code wird in viele Module mit separater Kompilierung aufgeteilt. Wenn jemand eine Inline-Funktion nur an einem Ort ändert, können die anderen Quellcodedateien die alte Version haben.

Wie man Verstößen vorbeugt:

  • Definieren Sie Funktionen außerhalb der Klasse nicht in der .h-Datei, wenn sie nicht inline sind.
  • Verwenden Sie Include-Guards und kontrollieren Sie den einzigen Ort der Definition.
  • Verwenden Sie für konstante Variablen constexpr oder, ab C++17, inline-Variablen.

Fangfrage

Darf man static-Funktionen (static void foo()) mit denselben Namen und unterschiedlicher Implementierung in verschiedenen .cpp-Dateien ohne Konsequenzen definieren?

Viele glauben, dass static-Funktionen untereinander in Modulen keinen Einfluss haben. Die Antwort lautet: ja, das ist erlaubt, da jede von ihnen internes Linkage hat (sichtbar nur innerhalb ihrer Übersetzungseinheit). Für Inline-Funktionen und Vorlagen ist dies jedoch nicht garantiert, was oft verwechselt wird.

Beispiel:

// file1.cpp static void foo() { std::cout << "A"; } // file2.cpp static void foo() { std::cout << "B"; }

Aufrufe in diesen Modulen werden unabhängig sein.

Beispiele für reale Fehler aufgrund von Unkenntnis der Feinheiten des Themas


Geschichte

In einem großen Projekt hat ein Entwickler den Körper einer Inline-Funktion nur in einer der Klone der .h-Datei geändert. Nach dem Kompilieren wurde ein Verhalten unvorhersehbar: Einige Module arbeiteten nach dem alten Algorithmus, andere nach dem neuen. Grund war der ODR-Verstoß für die Inline-Funktion.



Geschichte

Bei der Migration auf C++17 wurde eine Konstante in mehreren Header-Dateien ohne Verwendung des Schlüsselworts inline definiert. Bei der Linkerarbeit traten viele Duplicate-Symbol-Fehler auf. Dies wurde behoben, indem die Variable als inline const deklariert wurde.



Geschichte

Es wurde spät erkannt, dass die generierte .h-Datei des Build-Systems zwei Implementierungen derselben Methode einer Template-Klasse enthielt, die sich nur durch einen ifdef-Befehl in verschiedenen Builds unterschieden. Später fiel die Anwendung gelegentlich aufgrund von ABI-Abweichungen zwischen den verbundenen Modulen aus.