W C nie można mieć definicji / implementacji funkcji w pliku nagłówkowym. Jednak w C ++ możesz mieć pełną implementację metody w pliku nagłówkowym. Dlaczego zachowanie jest inne?
W C, jeśli zdefiniujesz funkcję w pliku nagłówka, wówczas funkcja ta pojawi się w każdym skompilowanym module zawierającym ten plik nagłówka, a dla funkcji zostanie wyeksportowany symbol publiczny. Jeśli więc dodatek funkcji jest zdefiniowany w nagłówku.h, a zarówno foo.c, jak i bar.c zawierają nagłówek.h, wówczas zarówno foo.o, jak i bar.o będą zawierać kopie dodatku.
Kiedy przejdziesz do połączenia tych dwóch plików obiektowych razem, linker zobaczy, że sumowanie symboli jest zdefiniowane więcej niż raz i nie pozwoli na to.
Jeśli zadeklarujesz funkcję jako statyczną, żaden symbol nie zostanie wyeksportowany. Pliki obiektowe foo.o i bar.o nadal będą zawierać osobne kopie kodu dla funkcji i będą mogły z nich korzystać, ale linker nie będzie w stanie zobaczyć żadnej kopii funkcji, więc nie będę narzekać. Oczywiście żaden inny moduł również nie będzie w stanie zobaczyć tej funkcji. A twój program będzie rozdęty dwiema identycznymi kopiami tej samej funkcji.
Jeśli zadeklarujesz funkcję tylko w pliku nagłówkowym, ale nie zdefiniujesz jej, a następnie zdefiniujesz tylko w jednym module, linker zobaczy jedną kopię funkcji, a każdy moduł w twoim programie będzie mógł ją zobaczyć i Użyj tego. Twój skompilowany program będzie zawierał tylko jedną kopię funkcji.
Tak więc możesz mieć definicję funkcji w pliku nagłówkowym w C, jest to po prostu zły styl, zła forma i ogólnie zły pomysł.
(Przez „zadeklaruj” mam na myśli zapewnienie prototypu funkcji bez ciała; przez „zdefiniowanie” mam na myśli faktyczny kod ciała funkcji; jest to standardowa terminologia w języku C.)
#ifndef HEADER_H
powinien zapobiec?C i C ++ zachowują się bardzo podobnie pod tym względem - możesz mieć
inline
funkcje w nagłówkach. W C ++ każda metoda, której treść znajduje się w definicji klasy, jest niejawnainline
. Jeśli chcesz zrobić to samo w C, zadeklaruj funkcjestatic inline
.źródło
static inline
” ... a nadal będziesz mieć wiele kopii funkcji w każdej jednostce tłumaczeniowej, która z niej korzysta. W C ++ zstatic
inline
niefunkcją będziesz miał tylko jedną kopię. Aby faktycznie mieć implementację w nagłówku w C, musisz 1) oznaczyć implementację jakoinline
(np.inline void func(){do_something();}
) I 2) faktycznie powiedzieć, że ta funkcja będzie w określonej jednostce tłumaczeniowej (npvoid func();
.).Pojęcie pliku nagłówkowego wymaga małego wyjaśnienia:
Albo podasz plik w wierszu poleceń kompilatora, albo zrobisz „#include”. Większość kompilatorów akceptuje plik poleceń z rozszerzeniem c, C, cpp, c ++ itp. Jako plik źródłowy. Zazwyczaj zawierają one jednak opcję wiersza polecenia, aby umożliwić użycie dowolnego dowolnego rozszerzenia do pliku źródłowego.
Zasadniczo plik podany w wierszu poleceń nosi nazwę „Źródło”, a dołączony plik nazywa się „Nagłówek”.
Krok preprocesora faktycznie bierze je wszystkie i sprawia, że wszystko wydaje się kompilatorowi jak jeden duży plik. To, co było w nagłówku lub w źródle, nie jest w tym momencie istotne. Zwykle istnieje opcja kompilatora, który może wyświetlić dane wyjściowe tego etapu.
Tak więc dla każdego pliku, który został podany w linii poleceń kompilatora, kompilator otrzymuje ogromny plik. Może zawierać kod / dane, które zajmują pamięć i / lub tworzą symbol, do którego można się odwoływać z innych plików. Teraz każdy z nich wygeneruje obraz „obiektu”. Linker może nadać „duplikatowi symbol”, jeśli ten sam symbol zostanie znaleziony w więcej niż dwóch plikach obiektowych, które są ze sobą połączone. Być może to jest powód; nie zaleca się umieszczania kodu w pliku nagłówkowym, który może tworzyć symbole w pliku obiektowym.
„Inline” są zwykle wstawiane. Ale podczas debugowania mogą nie być wstawiane. Dlaczego więc linker nie podaje wielokrotnie zdefiniowanych błędów? Proste ... Są to „słabe” symbole i tak długo, jak wszystkie dane / kod słabego symbolu ze wszystkich obiektów mają ten sam rozmiar i treść, połączony zachowa jedną kopię i upuści kopię z innych obiektów. To działa.
źródło
Możesz to zrobić w C99:
inline
funkcje są zapewnione gdzie indziej, więc jeśli funkcja nie została wstawiona, jej definicja jest tłumaczona na deklarację (tzn. Implementacja jest odrzucana). I oczywiście możesz użyćstatic
.źródło
C ++ standardowe cytaty
C ++ 17 N4659 standardowy projekt 10.1.6 „specyfikatorem inline” mówi, że metody są niejawnie inline:
a następnie w dalszej części widzimy, że metody wbudowane nie tylko mogą, ale muszą być zdefiniowane we wszystkich jednostkach tłumaczeniowych:
Jest to również wyraźnie wspomniane w nocie 12.2.1 „Funkcje członkowskie”:
Implementacja GCC 8.3
main.cpp
Kompiluj i przeglądaj symbole:
wynik:
następnie widzimy,
man nm
żeMyClass::myMethod
symbol jest oznaczony jako słaby w plikach obiektowych ELF, co oznacza, że może pojawić się w plikach wielu obiektów:źródło
Prawdopodobnie z tego samego powodu, dla którego musisz umieścić pełną implementację metody w definicji klasy w Javie.
Mogą wyglądać podobnie, z nawiasami kwadratowymi i wieloma takimi samymi słowami kluczowymi, ale są to różne języki.
źródło