Podczas może zawierać .cpppliki, jak wspomniano, jest to zły pomysł.
Jak wspomniałeś, deklaracje należą do plików nagłówkowych. Nie powodują one problemów, gdy są zawarte w wielu jednostkach kompilacji, ponieważ nie zawierają implementacji. Wielokrotne dołączenie definicji funkcji lub elementu klasy zwykle spowoduje problem (ale nie zawsze), ponieważ linker może się pomylić i zgłosić błąd.
Co powinno się zdarzyć, gdy każdy .cppplik zawiera definicje podzbioru programu, takie jak klasa, logicznie zorganizowana grupa funkcji, globalne zmienne statyczne (należy używać oszczędnie, jeśli w ogóle) itp.
Każda jednostka kompilacji ( .cppplik) zawiera następnie wszelkie deklaracje potrzebne do skompilowania zawartych w niej definicji. Śledzi funkcje i klasy, do których się odwołuje, ale nie zawiera, więc linker może je rozwiązać później, gdy połączy kod obiektowy w plik wykonywalny lub bibliotekę.
Przykład
Foo.h -> zawiera deklarację (interfejs) dla klasy Foo.
Foo.cpp -> zawiera definicję (implementację) dla klasy Foo.
Main.cpp-> zawiera główną metodę, punkt wejścia programu. Ten kod tworzy instancję Foo i używa jej.
Oba Foo.cppi Main.cppmuszą zawierać Foo.h. Foo.cpppotrzebuje go, ponieważ definiuje kod wspierający interfejs klasy, więc musi wiedzieć, czym jest ten interfejs. Main.cpppotrzebuje go, ponieważ tworzy Foo i wywołuje swoje zachowanie, więc musi wiedzieć, co to za zachowanie, rozmiar Foo w pamięci i jak znaleźć jego funkcje itp., ale nie potrzebuje jeszcze faktycznej implementacji.
Kompilator wygeneruje Foo.oz Foo.cppktórego zawiera cały kod klasy Foo w skompilowanej formie. Generuje również, Main.októra zawiera główną metodę i nierozwiązane odwołania do klasy Foo.
Teraz przychodzi łącznik, który łączy dwa pliki obiektów Foo.oi Main.odo pliku wykonywalnego. Widzi nierozwiązane odwołania do Foo, Main.oale Foo.ozawiera niezbędne symbole, więc „łączy kropki”, że tak powiem. Wywołanie funkcji Main.ojest teraz połączone z faktyczną lokalizacją skompilowanego kodu, więc w czasie wykonywania program może przejść do właściwej lokalizacji.
Gdybyś włączył Foo.cppplik Main.cpp, istniałyby dwie definicje klasy Foo. Linker zobaczyłby to i powiedział „Nie wiem, który wybrać, więc to jest błąd”. Krok kompilacji się powiódł, ale połączenie nie powiodło się. (Chyba że po prostu nie kompilujesz, Foo.cppale dlaczego znajduje się w osobnym .cpppliku?)
Wreszcie idea różnych typów plików nie ma znaczenia dla kompilatora C / C ++. Kompiluje „pliki tekstowe”, które, mam nadzieję, zawierają poprawny kod dla pożądanego języka. Czasami może być w stanie powiedzieć język na podstawie rozszerzenia pliku. Na przykład skompiluj .cplik bez opcji kompilatora, a on przyjmie C, podczas gdy a .cclub .cpprozszerzenie poinformuje go o założeniu C ++. Jednak mogę łatwo powiedzieć kompilatorowi, aby skompilował plik, a .hnawet .docxplik jako C ++, i wyemituje plik object ( .o), jeśli zawiera poprawny kod C ++ w formacie zwykłego tekstu. Te rozszerzenia są bardziej korzystne dla programisty. Jeśli widzę Foo.hi Foo.cpp, od razu zakładam, że pierwsza zawiera deklarację klasy, a druga zawiera definicję.