programowanieWiodący programista C++

Opowiedz o problemie 'One Definition Rule (ODR)' w C++. Jak wpływa to na duże projekty i jak unikać naruszeń?

Zdaj rozmowy kwalifikacyjne z asystentem AI Hintsage

Odpowiedź

One Definition Rule (ODR) — to podstawowa zasada C++, która wymaga, aby w obrębie całego programu (wszystkich jednostek tłumaczenia) dla każdego obiektu, funkcji lub klasy istniała dokładnie jedna zdefiniowana implementacja (definicja).

ODR jest naruszane, gdy:

  • W różnych plikach .cpp pojawiają się różne implementacje tej samej funkcji/klasy o różnym kodzie.
  • Funkcja inline lub szablon ma różne implementacje w różnych punktach dołączenia.

Prowadzi to do trudnych do wyśledzenia, nieprzewidywalnych błędów, niewłaściwego linkowania lub, co gorsza, do różnego zachowania programu w zależności od sposobu jego kompilacji.

Dlaczego dochodzi do naruszeń:

W dużych projektach często kopiowane/modyfikowane są pliki .h bez kontroli wersji lub dzieli się kod na wiele modułów z oddzielną kompilacją. Jeśli ktoś zmienia funkcję inline tylko w jednym miejscu, inne pliki źródłowe mogą mieć starą wersję.

Jak unikać:

  • Nie definiować funkcji poza klasą w pliku .h, jeśli nie są inline.
  • Używać include guards i kontrolować jedno miejsce definicji.
  • Dla zmiennych stałych używać constexpr lub, od C++17, inline variables.

Pytanie z podstępem

Czy można definiować static-funkcje (static void foo()) o tych samych nazwach i różnej implementacji w różnych plikach .cpp bez konsekwencji?

Wielu uważa, że static-funkcje w ogóle nie wpływają na siebie między modułami. Odpowiedź: tak, można, ponieważ każda z nich ma wewnętrzny linkage (widoczna tylko w swoim translation unit). Jednak dla funkcji inline i szablonów takie nie jest gwarantowane, co często wprowadza w błąd.

Przykład:

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

Wywołania w tych modułach będą niezależne.

Przykłady rzeczywistych błędów z powodu nieznajomości szczegółów tematu


Historia

W dużym projekcie jeden programista zmienił ciało funkcji inline tylko w jednym z klonów pliku .h. Po kompilacji niektóre zachowania stały się nieprzewidywalne: część modułów działała według starego algorytmu, część — według nowego. Powód — naruszenie ODR dla funkcji inline.



Historia

Podczas migracji na C++17 zmienna-stała była definiowana w kilku plikach nagłówkowych bez użycia słowa kluczowego inline. Podczas linkowania pojawiło się wiele błędów duplicate symbol. Naprawiono to, ogłaszając zmienną jako inline const.



Historia

Późno odkryto, że wygenerowany plik .h systemu kompilacji zawierał dwie implementacje tej samej metody w szablonowej klasie, różniące się jednym sprawdzeniem ifdef w różnych kompilacjach. Później aplikacja okresowo się zawieszała z powodu rozbieżności w ABI między powiązanymi modułami.