Kod C++ w plikach nagłówkowych

Mój osobisty styl w C++ zawsze musi umieszczać deklaracje klas w pliku include, a definicje w .plik cpp, bardzo podobny do Loki ' s answer to C++ Header Files, separacja kodu. Co prawda, część powodów, dla których lubię ten styl, prawdopodobnie ma związek ze wszystkimi latami spędzonymi na kodowaniu Modula-2 i Ada, które mają podobny schemat z plikami specyfikacji i plikami ciała.

Mam współpracownika, dużo bardziej obeznanego w C++ niż ja, który jest podkreślając, że wszystkie deklaracje C++ powinny, o ile to możliwe, zawierać definicje w pliku nagłówkowym. Nie mówi, że jest to poprawny alternatywny styl, czy nawet nieco lepszy styl, ale raczej jest to nowy powszechnie akceptowany styl, którego wszyscy używają teraz w C++.

Nie jestem tak zwinny, jak kiedyś, więc nie jestem naprawdę niespokojny, aby scrabble na ten bandwagon jego dopóki nie zobaczę kilka osób tam z nim. Jak powszechny jest ten idiom?

Po prostu dać jakąś strukturę odpowiedzi: czy to teraz Droga , bardzo powszechna, dość powszechna, niecodzienna, czy zwariowana?

Author: Community, 0000-00-00

14 answers

Twój współpracownik się myli, powszechnym sposobem jest i zawsze było wprowadzanie kodu .pliki cpp (lub dowolne rozszerzenie) i deklaracje w nagłówkach.

Czasami warto umieścić kod w nagłówku, co pozwala na bardziej sprytne wprowadzenie kodu przez kompilator. Ale w tym samym czasie, może zniszczyć twój czas kompilacji, ponieważ cały kod musi być przetwarzany za każdym razem, gdy jest dołączany przez kompilator.

Wreszcie, często denerwujące jest posiadanie kolistych relacji obiektów (czasami pożądane), gdy cały kod jest nagłówkiem.

Reasumując, miałeś rację, on się myli.

EDIT: zastanawiałem się nad twoim pytaniem. JestJeden przypadek, w którym to, co mówi, jest prawdą. szablony. Wiele nowszych" nowoczesnych "bibliotek, takich jak boost, w dużym stopniu korzysta z szablonów i często są "tylko nagłówkami"."Jednak należy to zrobić tylko wtedy, gdy mamy do czynienia z szablonami, ponieważ jest to jedyny sposób, aby to zrobić, gdy mamy do czynienia z nimi.

EDIT: niektóre ludzie chcieliby trochę więcej wyjaśnień, oto kilka przemyśleń na temat minusów pisania kodu "header only":

Jeśli przeszukasz, zobaczysz sporo osób próbujących znaleźć sposób na skrócenie czasu kompilacji podczas radzenia sobie z boostem. Na przykład: Jak skrócić czas kompilacji za pomocą Boost ASIO, czyli kompilacji 14S pojedynczego pliku 1K z dołączonym boostem. 14s może nie wydawać się "eksplodujące", ale na pewno jest o wiele dłuższy niż typowy i może się sumować dość szybko. Kiedy mamy do czynienia z dużym projektem. Tylko biblioteki nagłówkowe wpływają na czas kompilacji w dość wymierny sposób. Tolerujemy to, bo boost jest tak przydatny.

DODATKOWO jest wiele rzeczy, których nie można zrobić tylko w nagłówkach (nawet boost ma biblioteki, do których trzeba się połączyć dla pewnych części, takich jak wątki, system plików itp.). Podstawowym przykładem jest to, że nie możesz mieć prostych obiektów globalnych tylko w nagłówku libs (chyba że uciekniesz się do obrzydliwości, która jest singletonem) ponieważ napotkasz wiele błędów definicji. uwaga: zmienne wbudowane w C++17 sprawią, że ten przykład będzie możliwy do wykonania w przyszłości.

Jako ostatni punkt, przy użyciu boost jako przykład kodu tylko nagłówka, ogromny szczegół często zostaje pominięty.

Boost to biblioteka, Nie kod poziomu użytkownika. więc nie zmienia się tak często. W kodzie użytkownika, jeśli umieścisz wszystko w nagłówkach, każda mała zmiana spowoduje, że będziesz musiał przekompilować cały projekt. To monumentalne marnotrawstwo czas (i nie jest tak w przypadku bibliotek, które nie zmieniają się z kompilacji na kompilację). Kiedy dzielisz rzeczy między nagłówek/źródło i jeszcze lepiej, Użyj deklaracji forward, aby zmniejszyć zawartość, możesz zaoszczędzić godziny rekompilacji po zsumowaniu w ciągu dnia.

 176
Author: Evan Teran,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:47:14

W dniu, w którym programiści C++ uzgodnią drogę, jagnięta będą leżeć z lwami, Palestyńczycy obejmą Izraelczyków, a koty i psy będą mogły się żenić.

Separacja między .h i .pliki cpp są w większości arbitralne w tym momencie, pozostałość optymalizacji kompilatora dawno temu. Moim zdaniem deklaracje należą do nagłówka, a definicje do pliku implementacji. Ale to tylko nawyk, nie religia.

 132
Author: Yes - that Jake.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-24 19:48:26

Kod w nagłówkach jest ogólnie złym pomysłem, ponieważ wymusza rekompilację wszystkich plików zawierających nagłówek, gdy zmienisz rzeczywisty kod, a nie deklaracje. Spowolni to również kompilację, ponieważ będziesz musiał przeanalizować kod w każdym pliku zawierającym nagłówek.

Powodem posiadania kodu w plikach nagłówkowych jest to, że zwykle jest on potrzebny do poprawnego działania słowa kluczowego inline oraz podczas korzystania z szablonów, które są instancjonowane w innych plikach cpp.

 22
Author: Laserallan,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-24 19:49:34

To, co może Cię poinformować, to przekonanie, że większość kodu C++ powinna być templowana, aby umożliwić maksymalną użyteczność. A jeśli jest szablonem, to wszystko będzie musiało znajdować się w pliku nagłówkowym, aby kod klienta mógł go zobaczyć i utworzyć instancję. Jeśli jest wystarczająco dobre dla Boost i STL, to jest wystarczająco dobre dla nas.

Nie zgadzam się z tym punktem widzenia, ale może być tam, skąd pochodzi.

 17
Author: JohnMcG,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-24 20:08:25

Myślę, że twój współpracownik jest mądry i masz również rację.

Przydatne rzeczy, które odkryłem, że umieszczanie wszystkiego w nagłówkach jest takie, że:

  1. Nie ma potrzeby pisania i synchronizacji nagłówków i źródeł.

  2. Struktura jest prosta i żadne koliste zależności nie zmuszają kodera do stworzenia "lepszej" struktury.

  3. Przenośny, łatwy do osadzenia w nowym projekcie.

Zgadzam się z problemem czasu kompilacji, ale myślę, że powinniśmy zauważ, że:

  1. Zmiana pliku źródłowego jest bardzo prawdopodobna, aby zmienić pliki nagłówkowe, co prowadzi do ponownego skompilowania całego projektu.

  2. 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. Rozdzielenie zadań na różne projekty i moduł może uniknąć tego problemu.

Na koniec chcę tylko wspierać twojego współpracownika, tylko moim osobistym zdaniem.

 12
Author: XU Bin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-12-14 17:35:44

Często umieszczam trywialne funkcje członkowskie w pliku nagłówkowym, aby umożliwić ich inlinowanie. Ale żeby umieścić tam cały kod, żeby był zgodny z szablonami? To zwykłe szaleństwo.

Pamiętaj: głupia konsystencja jest hobgoblinem małych umysłów .

 12
Author: Mark Ransom,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-03 18:26:34

Ogólnie rzecz biorąc, pisząc nową klasę, umieszczę cały kod w klasie, więc nie muszę szukać go w innym pliku.. Gdy wszystko działa, rozkładam ciało metod do pliku cpp, pozostawiając prototypy w pliku hpp.

 5
Author: EvilTeach,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-24 21:50:30

Jak powiedział Tuomas, Twój nagłówek powinien być minimalny. Aby być kompletnym, poszerzę trochę.

Osobiście używam 4 typów plików w moich projektach C++:

  • Public:
  • nagłówek przekierowujący: w przypadku szablonów itp., plik ten otrzymuje deklaracje przekierowujące, które pojawią się w nagłówku.
  • Header: ten plik zawiera nagłówek przekierowania, jeśli istnieje, i deklaruje wszystko, co chcę być publiczne (i definiuje klasy...)
  • prywatne:
  • Private header: ten plik jest nagłówkiem zarezerwowanym dla implementacji, zawiera nagłówek i deklaruje funkcje / struktury pomocnicze (na przykład dla pimpl lub predykatów). Pomiń, jeśli jest to niepotrzebne.
  • plik źródłowy: zawiera nagłówek prywatny (lub nagłówek, jeśli nie ma nagłówka prywatnego) i definiuje wszystko (nie-szablon...)

Ponadto łączę to z inną zasadą: nie Definiuj tego, co możesz zadeklarować dalej. Choć oczywiście jestem tam rozsądny (używanie pimpl wszędzie jest dość kłopotliwe).

Oznacza to, że wolę deklarację forward niż dyrektywę #include w nagłówkach, kiedy tylko mogę z nimi uciec.

Na koniec używam również reguły widoczności: ograniczam zakresy moich symboli tak bardzo, jak to możliwe, aby nie zanieczyszczały zewnętrznych zakresów.

Ujmując to w całości:

// example_fwd.hpp
// Here necessary to forward declare the template class,
// you don't want people to declare them in case you wish to add
// another template symbol (with a default) later on
class MyClass;
template <class T> class MyClassT;

// example.hpp
#include "project/example_fwd.hpp"

// Those can't really be skipped
#include <string>
#include <vector>

#include "project/pimpl.hpp"

// Those can be forward declared easily
#include "project/foo_fwd.hpp"

namespace project { class Bar; }

namespace project
{
  class MyClass
  {
  public:
    struct Color // Limiting scope of enum
    {
      enum type { Red, Orange, Green };
    };
    typedef Color::type Color_t;

  public:
    MyClass(); // because of pimpl, I need to define the constructor

  private:
    struct Impl;
    pimpl<Impl> mImpl; // I won't describe pimpl here :p
  };

  template <class T> class MyClassT: public MyClass {};
} // namespace project

// example_impl.hpp (not visible to clients)
#include "project/example.hpp"
#include "project/bar.hpp"

template <class T> void check(MyClass<T> const& c) { }

// example.cpp
#include "example_impl.hpp"

// MyClass definition

Ratunkiem jest to, że w większości przypadków nagłówek forward jest bezużyteczny: konieczne jest tylko w przypadku typedef lub template i tak jest nagłówek implementacji;)

 5
Author: Matthieu M.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-03-26 15:17:38

Jeśli ta nowa droga jest naprawdę drogą , w naszych projektach mogliśmy podążać w innym kierunku.

Ponieważ staramy się unikać wszystkich niepotrzebnych rzeczy w nagłówkach. Obejmuje to unikanie kaskady nagłówka. Kod w nagłówkach prawdopodobnie będzie potrzebował innego nagłówka, który będzie potrzebował innego nagłówka i tak dalej. Jeśli jesteśmy zmuszeni do używania szablonów, staramy się unikać zaśmiecania nagłówków zbyt dużą ilością szablonów.

Używamy również "nieprzezroczysty wskaźnik" - wzorzec w stosownych przypadkach.

Dzięki tym praktykom możemy tworzyć szybciej niż większość naszych rówieśników. I tak... zmiana kodu lub członków klasy nie spowoduje ogromnych przebudów.

 4
Author: Virne,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-24 20:07:36

Aby dodać więcej zabawy można dodać .ipp pliki, które zawierają implementację szablonu( który jest zawarty w .hpp), natomiast .hpp zawiera interfejs.

Jako że oprócz kodu templatowanego (w zależności od projektu może to być Większość lub mniejszość plików) istnieje zwykły kod i tutaj lepiej jest oddzielić deklaracje i definicje. W razie potrzeby należy podać również deklaracje typu forward - może to mieć wpływ na czas kompilacji.

 4
Author: Anonymous,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-24 20:52:55

Ja osobiście robię to w moich plikach nagłówkowych:

// class-declaration

// inline-method-declarations

Nie lubię mieszać kodu metod z klasą, ponieważ uważam, że szybkie szukanie rzeczy jest bolesne.

Nie umieszczałbym wszystkich metod w pliku nagłówkowym. Kompilator (normalnie) nie będzie w stanie wstawiać wirtualnych metod i będzie (prawdopodobnie) tylko małe metody bez pętli (całkowicie zależy od kompilatora).

Wykonywanie metod w klasie jest poprawne... ale z czytelnego punktu widzenia ja nie podoba mi się to. Umieszczenie metod w nagłówku oznacza, że jeśli to możliwe, zostaną one zainlinowane.

 4
Author: TofuBeer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-27 10:28:21

IMHO, ma zasługi tylko wtedy, gdy robi szablony i / lub metaprogramowanie. Jest już wiele powodów, dla których ograniczasz pliki nagłówkowe tylko do deklaracji. Po prostu... nagłówki. Jeśli chcesz dołączyć kod, skompiluj go jako bibliotekę i połącz.

 2
Author: spoulson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-25 12:54:09

Usunąłem całą implementację z definicji klasy. Chcę mieć komentarze doxygen z definicji klasy.

 2
Author: Jesus Fernandez,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-09-30 11:31:26

Czy to naprawdę nie zależy od złożoności systemu i wewnętrznych konwencji?

W tej chwili pracuję nad symulatorem sieci neuronowej, który jest niezwykle złożony, a przyjęty styl, którego mam używać, to:

Definicje klas w nazwach klas.h
Kod klasy w classnameCode.h
kod wykonywalny w nazwie klasy.cpp

To dzieli symulacje zbudowane przez Użytkownika od klas bazowych zbudowanych przez deweloperów i działa najlepiej w sytuacja.

Zdziwiłbym się jednak widząc, że ludzie robią to np. w aplikacji graficznej, czy jakiejkolwiek innej aplikacji, której celem nie jest dostarczenie użytkownikom bazy kodu.

 1
Author: Ed James,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-02-25 15:15:21