Mój osobisty styl z C ++ zawsze umieszczał deklaracje klas w pliku dołączanym, a definicje w pliku .cpp, bardzo podobnie, jak określono w odpowiedzi Lokiego na pliki nagłówkowe C ++, Separacja kodów . Trzeba przyznać, że część tego, co podoba mi się w tym stylu, prawdopodobnie wiąże się z tymi wszystkimi latami, które spędziłem na kodowaniu Modula-2 i Ady, które mają podobny schemat z plikami specyfikacji i plikami body.
Mam współpracownika, znacznie bardziej znającego się na C ++ niż ja, który nalega, aby wszystkie deklaracje C ++ zawierały, tam gdzie to możliwe, definicje zawarte w pliku nagłówkowym. Nie twierdzi, że jest to poprawny styl alternatywny lub nawet nieco lepszy styl, ale raczej jest to nowy, powszechnie akceptowany styl, którego wszyscy używają w C ++.
Nie jestem już tak powściągliwy jak kiedyś, więc tak naprawdę nie mam ochoty wskoczyć na jego modę, dopóki nie zobaczę tam kilku innych ludzi. Więc jak często ten idiom jest naprawdę powszechny?
Żeby udzielić odpowiedzi na pytania: czy teraz jest to Droga , bardzo powszechna, nieco powszechna, niecodzienna czy szalona?
źródło
Odpowiedzi:
Twój współpracownik się myli, powszechnym sposobem jest i zawsze było umieszczanie kodu w plikach .cpp (lub jakimkolwiek innym rozszerzeniu, które lubisz) i deklaracjach w nagłówkach.
Czasami wstawianie kodu do nagłówka ma pewne zalety, co może pozwolić na bardziej sprytne wstawianie przez kompilator. Ale jednocześnie może zniszczyć Twoje czasy kompilacji, ponieważ cały kod musi być przetwarzany za każdym razem, gdy jest uwzględniany przez kompilator.
Wreszcie, często denerwujące są okrągłe relacje między obiektami (czasem pożądane), gdy cały kod jest nagłówkiem.
Podsumowując, miałeś rację, on się myli.
EDYCJA: Myślałem o twoim pytaniu. Jest jeden przypadek, w którym to, co mówi, jest prawdą. szablony. Wiele nowszych „nowoczesnych” bibliotek, takich jak boost, intensywnie wykorzystuje szablony i często są one „tylko nagłówkami”. Należy to jednak zrobić tylko w przypadku szablonów, ponieważ jest to jedyny sposób, aby to zrobić.
EDYCJA: Niektórzy chcieliby trochę więcej wyjaśnień, oto kilka minusów pisania kodu „tylko nagłówek”:
Jeśli się rozejrzysz, zobaczysz, że sporo osób próbuje znaleźć sposób na skrócenie czasu kompilacji podczas pracy z boostem. Na przykład: Jak skrócić czas kompilacji za pomocą Boost Asio , który widzi kompilację 14s pojedynczego pliku 1K z dołączonym boostem. 14s może nie wydawać się „eksplodować”, ale z pewnością jest znacznie dłuższy niż typowy i może zsumować się dość szybko. W przypadku dużego projektu. Biblioteki tylko nagłówka wpływają na czasy kompilacji w dość wymierny sposób. Po prostu tolerujemy to, ponieważ wzmocnienie jest tak przydatne.
Ponadto istnieje wiele rzeczy, których nie można zrobić tylko w nagłówkach (nawet boost ma biblioteki, do których należy się połączyć w przypadku niektórych części, takich jak wątki, system plików itp.). Podstawowym przykładem jest to, że nie można mieć prostych obiektów globalnych w bibliotekach tylko nagłówkowych (chyba że skorzystasz z obrzydliwości, która jest singletonem), ponieważ napotkasz błędy definicji. UWAGA: Zmienne wbudowane w C ++ 17 sprawią, że ten konkretny przykład będzie wykonalny w przyszłości.
Na koniec, gdy używasz wzmocnienia jako przykładu kodu zawierającego tylko nagłówek, często pomija się ogromny szczegół.
Boost to biblioteka, a nie kod poziomu użytkownika. więc to nie zmienia się tak często. Jeśli w kodzie użytkownika umieścisz wszystko w nagłówkach, każda drobna zmiana spowoduje konieczność ponownej kompilacji całego projektu. To ogromna strata czasu (i nie dotyczy to bibliotek, które nie zmieniają się z kompilacji na kompilację). Kiedy dzielisz rzeczy między nagłówek / źródło, a jeszcze lepiej, użyj deklaracji przesyłania dalej, aby zmniejszyć liczbę dołączeń, możesz zaoszczędzić godziny ponownej kompilacji, jeśli zostaną dodane w ciągu jednego dnia.
źródło
W dniu, w którym koderzy C ++ uzgodnią Drogę , jagnięta położy się z lwami, Palestyńczycy przyjmą Izraelczyków, a koty i psy będą mogły wyjść za mąż.
Oddzielenie plików .h i .cpp jest w tym momencie w większości arbitralne, co pozostało po optymalizacji kompilatora. Moim zdaniem deklaracje należą do nagłówka, a definicje do pliku implementacyjnego. Ale to tylko nawyk, nie religia.
źródło
Kod w nagłówkach jest ogólnie złym pomysłem, ponieważ wymusza rekompilację wszystkich plików, które zawierają nagłówek, gdy zmieniasz rzeczywisty kod, a nie deklaracje. Spowolni również kompilację, ponieważ będziesz musiał przeanalizować kod w każdym pliku zawierającym nagłówek.
Powodem, dla którego kod w plikach nagłówkowych jest potrzebny, jest to na ogół potrzebne do poprawnego działania słowa kluczowego w wierszu i podczas korzystania z szablonów, które są tworzone w innych plikach CPP.
źródło
To, co może informować współpracownika, to pogląd, że większość kodu C ++ powinna być szablonowana, aby umożliwić maksymalną użyteczność. A jeśli jest szablonowy, wszystko musi znajdować się w pliku nagłówkowym, aby kod klienta mógł go zobaczyć i utworzyć go. Jeśli jest wystarczająco dobry dla Boost i STL, jest dla nas wystarczająco dobry.
Nie zgadzam się z tym punktem widzenia, ale może być skąd pochodzi.
źródło
Myślę, że twój współpracownik jest mądry i ty też masz rację.
Przydatne rzeczy, które znalazłem, umieszczając wszystko w nagłówkach, to:
Nie trzeba pisać i synchronizować nagłówków i źródeł.
Struktura jest prosta i brak zależności między kołami zmusza koder do stworzenia „lepszej” struktury.
Przenośny, łatwy do osadzenia w nowym projekcie.
Zgadzam się z problemem czasu kompilacji, ale myślę, że powinniśmy zauważyć, że:
Zmiana pliku źródłowego najprawdopodobniej zmieni pliki nagłówkowe, co prowadzi do ponownej kompilacji całego projektu.
Szybkość kompilacji jest znacznie szybsza niż wcześniej. A jeśli masz projekt do zbudowania z długim czasem i wysoką częstotliwością, może to oznaczać, że Twój projekt ma wady. Rozdziel zadania na różne projekty, a moduł może uniknąć tego problemu.
Na koniec chcę tylko wspierać waszego współpracownika, tylko z mojego osobistego punktu widzenia.
źródło
Często umieszczam w pliku nagłówkowym trywialne funkcje składowe, aby umożliwić ich wstawianie. Ale żeby umieścić tam cały kod, żeby zachować spójność z szablonami? To zwykłe orzechy.
Pamiętaj: głupia konsekwencja to hobgoblin małych umysłów .
źródło
A<B>
treść klasy szablonu w pliku CPP, a następnie użytkownik będzie chciałA<C>
?Jak powiedział Tuomas, nagłówek powinien być minimalny. Aby zakończyć, rozwinę się nieco.
Osobiście korzystam z 4 typów plików w moich
C++
projektach:Ponadto łączę to z inną regułą: nie definiuj tego, co możesz przesłać dalej deklarować. Chociaż oczywiście jestem tam rozsądny (używanie Pimpl wszędzie jest dość kłopotliwe).
Oznacza to, że wolę deklarację przekazania zamiast
#include
dyrektywy w moich nagłówkach, ilekroć będę w stanie uciec od nich.Na koniec stosuję również zasadę widoczności: ograniczam zakresy moich symboli tak bardzo, jak to możliwe, aby nie zanieczyszczały zewnętrznych zakresów.
Podsumowując:
Ratownik jest to, że większość czasu nagłówek przodu jest bezużyteczny: konieczne tylko w przypadku
typedef
albotemplate
i tak jest nagłówek realizacja;)źródło
Aby dodać więcej zabawy, możesz dodać
.ipp
pliki, które zawierają implementację szablonu (która jest uwzględniona.hpp
), podczas gdy.hpp
zawiera interfejs.Ponieważ poza kodowanym szablonem (w zależności od projektu może to być większość lub mniejszość plików) istnieje normalny kod i tutaj lepiej jest oddzielić deklaracje i definicje. W razie potrzeby podaj także deklaracje forward - może to mieć wpływ na czas kompilacji.
źródło
Zasadniczo, pisząc nową klasę, umieszczam cały kod w klasie, więc nie muszę szukać innego pliku. Po tym, jak wszystko działa, rozkładam treść metod na plik cpp , pozostawiając prototypy w pliku hpp.
źródło
Jeśli ten nowy sposób jest naprawdę The Way , moglibyśmy podążać w innym kierunku w naszych projektach.
Ponieważ staramy się unikać wszystkich niepotrzebnych rzeczy w nagłówkach. Obejmuje to unikanie kaskady nagłówków. Kod w nagłówkach będzie prawdopodobnie wymagał dołączenia innego nagłówka, który będzie wymagał innego nagłówka i tak dalej. Jeśli jesteśmy zmuszeni do korzystania z szablonów, staramy się zbytnio nie zaśmiecać nagłówków elementami szablonu.
W razie potrzeby używamy również „nieprzezroczystego wskaźnika” .
Dzięki tym praktykom możemy wykonywać szybsze kompilacje niż większość naszych rówieśników. I tak ... zmiana kodu lub członków klasy nie spowoduje wielkich przebudów.
źródło
Osobiście robię to w moich plikach nagłówkowych:
Nie lubię mieszać kodu metod z klasą, ponieważ trudno mi jest szybko znaleźć rzeczy.
Nie umieściłbym WSZYSTKICH metod w pliku nagłówkowym. Kompilator (normalnie) nie będzie w stanie wstawiać metod wirtualnych i (prawdopodobnie) będzie wstawiał tylko małe metody bez pętli (całkowicie zależy od kompilatora).
Robienie metod w klasie jest poprawne ... ale z czytelnego punktu widzenia nie podoba mi się to. Umieszczenie metod w nagłówku oznacza, że jeśli to możliwe, zostaną wstawione.
źródło
IMHO, Zasługuje TYLKO, jeśli robi szablony i / lub metaprogramowanie. Jest już wiele powodów, dla których ograniczasz pliki nagłówkowe do samych deklaracji. Są po prostu… nagłówkami. Jeśli chcesz dołączyć kod, skompiluj go jako bibliotekę i połącz z nim.
źródło
Wyciągnąłem całą implementację z definicji klasy. Chcę mieć komentarze doxygen z definicji klasy.
źródło
Myślę, że absolutnie absurdalne jest umieszczenie WSZYSTKICH definicji funkcji w pliku nagłówkowym. Czemu? Ponieważ plik nagłówkowy jest używany jako interfejs PUBLICZNY dla twojej klasy. To zewnętrzna część „czarnej skrzynki”.
Kiedy musisz spojrzeć na klasę, aby dowiedzieć się, jak z niej korzystać, powinieneś spojrzeć na plik nagłówka. Plik nagłówkowy powinien zawierać listę tego, co może zrobić (skomentowany, aby opisać szczegóły korzystania z każdej funkcji) i powinien zawierać listę zmiennych składowych. NIE POWINIEN OBEJMOWAĆ JAK każda poszczególna funkcja jest zaimplementowana, ponieważ jest to ładunek niepotrzebnych informacji i tylko zaśmieca plik nagłówkowy.
źródło
Czy to naprawdę nie zależy od złożoności systemu i wewnętrznych konwencji?
Obecnie pracuję nad niezwykle złożonym symulatorem sieci neuronowej, a akceptowanym stylem, którego mam się spodziewać, jest:
Definicje klas w classname.h
Kod klasy w classnameCode.h
kod wykonywalny w classname.cpp
Dzieli to symulacje zbudowane przez użytkownika od klas bazowych opracowanych przez programistów i działa najlepiej w danej sytuacji.
Byłbym jednak zaskoczony, gdy ludzie robią to w aplikacji graficznej lub w jakiejkolwiek innej aplikacji, której celem nie jest zapewnienie użytkownikom bazy kodu.
źródło
Kod szablonu powinien znajdować się tylko w nagłówkach. Poza tym wszystkie definicje z wyjątkiem wstawiania powinny być w .cpp. Najlepszym argumentem za tym byłyby implementacje standardowej biblioteki, które stosują tę samą regułę. Nie zgodziłbyś się, że deweloperzy std lib mieliby rację w tej sprawie.
źródło
libstdc++
Wydaje się , że GCC (AFAICS) nie umieszcza prawie nicsrc
i prawie wszystkiegoinclude
, niezależnie od tego, czy „musi” być w nagłówku. Więc nie sądzę, że jest to dokładne / przydatne odniesienie. W każdym razie, nie sądzę, aby stdlibs były wzorem dla kodu użytkownika: są one oczywiście napisane przez wysoko wykwalifikowanych programistów, ale należy ich używać , a nie czytać: usuwają dużą złożoność, o której większość programistów nie powinna myśleć ,_Reserved
__names
wszędzie są brzydkie, aby uniknąć konfliktów z użytkownikiem, komentarze i odstępy są poniżej tego, co radziłbym itp. Są wzorowe w wąskim zakresie.Myślę, że twój współpracownik ma rację, dopóki nie wejdzie w proces pisania kodu wykonywalnego w nagłówku. Wydaje mi się, że właściwą równowagą jest podążanie ścieżką wskazaną przez GNAT Ada, gdzie plik .ads zapewnia całkowicie odpowiednią definicję interfejsu pakietu dla jego użytkowników i jego dzieci.
Nawiasem mówiąc, Ted, czy na tym forum zapoznałeś się z ostatnim pytaniem na temat powiązania Ady z biblioteką CLIPS, które napisałeś kilka lat temu i które nie jest już dostępne (odpowiednie strony internetowe są teraz zamknięte). Nawet jeśli zostanie wykonane w starej wersji Clips, to powiązanie może być dobrym przykładem na początek dla kogoś, kto chce korzystać z mechanizmu wnioskowania CLIPS w programie Ada 2012.
źródło