it-swarm-eu.dev

Proč máte definici metody uvnitř souboru záhlaví v C ++, když v C nemůžete?

V C nemůžete mít definici/implementaci funkce uvnitř souboru záhlaví. V C++ však můžete mít úplnou implementaci metod uvnitř souboru záhlaví. Proč je chování odlišné?

25
Joshua Partogi

Pokud v C definujete funkci v souboru záhlaví, objeví se tato funkce v každém kompilovaném modulu, který obsahuje tento soubor záhlaví, a veřejný symbol se pro tuto funkci exportuje. Pokud je tedy funkce additup definována v hlavičce header.h a foo.c a bar.c zahrnují header.h, potom foo.o a bar.o budou zahrnovat kopie additupu.

Když propojíte tyto dva soubory souborů dohromady, linker uvidí, že doplněk symbolu je definován více než jednou, a nedovolí to.

Pokud deklarujete funkci jako statickou, nebude exportován žádný symbol. Soubory objektů foo.o a bar.o budou stále obsahovat samostatné kopie kódu funkce a budou je moci používat, ale linker nebude moci vidět žádnou kopii funkce, takže bude nebude si stěžovat. Funkce samozřejmě nebude moci vidět ani žádný jiný modul. A váš program bude nafouknut dvěma identickými kopiemi stejné funkce.

Pokud funkci deklarujete pouze v hlavičkovém souboru, ale nedefinujete ji a poté ji definujete pouze v jednom modulu, linker uvidí jednu kopii funkce a každý modul ve vašem programu ji bude moci vidět a použij to. A váš kompilovaný program bude obsahovat pouze jednu kopii funkce.

Takže můžete máte definici funkce v souboru záhlaví v C, je to jen špatný styl, špatný tvar a všestranný špatný nápad.

(„Deklarovat“ mám na mysli poskytnutí prototypu funkce bez těla; „definováním“ mám na mysli poskytnutí skutečného kódu funkce; to je standardní terminologie C).

29
David Conrad

C a C++ se v tomto ohledu chovají velmi podobně - v záhlaví můžete mít funkce inline. V C++ je každá metoda, jejíž tělo je uvnitř definice třídy, implicitně inline. Pokud chcete udělat totéž v C, deklarujte funkce static inline.

30
Simon Richter

Koncept souboru záhlaví vyžaduje trochu vysvětlení:

Buď zadáte soubor na příkazovém řádku kompilátoru, nebo uděláte '#include'. Většina kompilátorů přijímá příkazový soubor s příponou c, C, cpp, c ++ atd. Jako zdrojový soubor. Obvykle však obsahují možnost příkazového řádku, která umožňuje použití libovolného rozšíření zdrojového souboru.

Obecně se soubor uvedený na příkazovém řádku nazývá 'Zdroj' a soubor, který je součástí, se nazývá 'Záhlaví'.

Krok preprocesoru je ve skutečnosti vezme všechny a způsobí, že se kompilátoru všechno objeví jako jediný velký soubor. To, co bylo v záhlaví nebo ve zdroji, není v tomto okamžiku relevantní. Obvykle existuje možnost kompilátoru, který může zobrazit výstup této fáze.

Takže pro každý soubor, který byl uveden na příkazovém řádku kompilátoru, je kompilátoru dán obrovský soubor. To může mít kód/data, která zabírají paměť a/nebo vytvoří symbol, na který se bude odkazovat z jiných souborů. Nyní každá z nich vygeneruje obraz „objektu“. Linker může dát 'duplicitní symbol', pokud je stejný symbol nalezen ve více než dvou souborech objektů, které jsou propojeny dohromady. Možná to je důvod; nedoporučuje se vkládat kód do souboru záhlaví, který může v objektu objektu vytvářet symboly.

'Inline' jsou obvykle inline .. ale při ladění nemusí být inline. Proč tedy linker nedává mnohonásobně definované chyby? Jednoduché ... Jedná se o „slabé“ symboly a pokud všechna data/kód slabého symbolu ze všech objektů mají stejnou velikost a obsah, propojený bude udržovat jednu kopii a kopii z ostatních objektů. Funguje to.

7
vrdhn

Můžete to udělat v C99: Funkce inline jsou zaručeny, že budou poskytovány někde jinde, takže pokud funkce nebyla inline, její definice se převede do deklarace (tj. Implementace se zahodí). A samozřejmě můžete použít static.

4
SK-logic

Standardní citace C++

C++ 17 N4659 standardní koncept 10.1.6 „Inline specifier“ říká, že metody jsou implicitně inline:

4 Funkce definovaná v definici třídy je inline funkce.

a dále dolů vidíme, že inline metody mohou být nejen, ale musí definovány na všech překladových jednotkách:

6. Inline funkce nebo proměnná musí být definována v každé překladové jednotce, ve které je používána nebo odr, a musí mít v každém případě přesně stejnou definici (6.2).

To je také výslovně uvedeno v poznámce v 12.2.1 „Členské funkce“:

1 Členská funkce může být definována (11.4) ve své definici třídy, v tom případě je to vložená členská funkce (10.1.6) [...]

3 [Poznámka: V programu může být nanejvýš jedna definice funkce nevloženého člena. V programu může být více než jedna definice vložené členské funkce. Viz 6.2 a 10.1.6. - koncová poznámka]

implementace GCC 8.3

main.cpp

struct MyClass {
    void myMethod() {}
};

int main() {
    MyClass().myMethod();
}

Kompilace a zobrazení symbolů:

g++ -c main.cpp
nm -C main.o

výstup:

                 U _GLOBAL_OFFSET_TABLE_
0000000000000000 W MyClass::myMethod()
                 U __stack_chk_fail
0000000000000000 T main

pak z man nm vidíme, že symbol MyClass::myMethod je označen jako slabý u objektových souborů ELF, což znamená, že se může objevit na více objektových souborech:

"W" "w" Symbol je slabý symbol, který nebyl specificky označen jako symbol slabého objektu. Pokud je slabě definovaný symbol spojen s normálně definovaným symbolem, použije se normálně definovaný symbol bez chyby. Když je spojen slabý nedefinovaný symbol a není definován, je hodnota symbolu stanovena systémově specifickým způsobem bez chyby. V některých systémech velká písmena označují, že byla zadána výchozí hodnota.