2009-05-17 20 views
14

Zastanawiam się, jaki jest sens oddzielania klas do pliku .h i .cpp? Utrudnia to edycję, a jeśli twoja klasa nie zostanie skompilowana do pliku .lib lub .dll do użytku zewnętrznego, jaki jest sens?Po co umieszczać deklarację klasową i definicję w dwóch osobnych plikach w C++?

Edycja: Powodem, dla którego pytam, jest to, że biblioteki Boost umieszczają wszystko w pliku .hpp, (większość bibliotek w każdym razie), i chciałem wiedzieć, dlaczego jest on oddzielony w większości innych kodów, które Widzę.

Odpowiedz

15

C++ ma coś, co nazywa się regułą jednej definicji. Oznacza to, że (z wyłączeniem funkcji wbudowanych) definicje mogą pojawiać się tylko w jednej jednostce kompilacji. Ponieważ pliki nagłówkowe C++ są po prostu "kopiowane i wklejane" przy każdym pliku włączającym, teraz umieszczasz definicje w wielu miejscach, jeśli umieścisz definicje w plikach nagłówkowych.

Oczywiście, możesz powiedzieć, dlaczego nie zrobić wszystkiego w linii. Jeśli kompilator przestrzega Twojej sugestii liniowej, kod dla długich funkcji zostanie zreplikowany na każdej stronie wywołania, co spowoduje, że twój kod będzie nadmiernie duży i prawdopodobnie spowoduje kłopotliwe, problemy z pamięcią podręczną i wszelkiego rodzaju niezamierzone rzeczy.

Z drugiej strony, jeśli kompilator nie słucha ciebie i niczego nie inline, teraz masz 2 problemy: 1) nie wiesz, która jednostka tłumaczeniowa ma twoje definicje klas, i 2) kompilator wciąż musi przeglądać twoje definicje za każdym razem, gdy je #include. Co więcej, nie ma łatwego sposobu, aby upewnić się, że przypadkowo nie zdefiniowałeś tej samej metody dwukrotnie, w 2 różnych nagłówkach, inaczej.

Otrzymujesz również problem z zależnością cykliczną. Aby klasa mogła wywołać metodę innej klasy, ta klasa musi zostać zadeklarowana jako pierwsza. Jeśli więc 2 klasy muszą wywoływać swoje metody, każda z nich musi zostać zadeklarowana, zanim będzie można je zdefiniować. Nie ma sposobu, aby to zrobić z deklaracjami i definicjami w jednym pliku.

Naprawdę, w ten sposób powstał język i parser. To ból, ale musisz sobie z tym poradzić.

0

Ponieważ w większości przypadków będziesz chciał użyć klasy gdzieś poza plikiem, w którym ją zaimplementujesz. Jeśli stworzysz cały program w jednym pliku, nie potrzebujesz separacji.

Rzadko chcesz napisać program C++ w jednym pliku.

+0

Chciałem umieścić definicję całej klasy w .h i włączając ją, jednocześnie chroniąc ją za pomocą nagłówków –

+0

co się stanie, jeśli włączysz ten plik .h w więcej niż jednym miejscu w tym samym pliku wykonywalnym? Ans: kończysz z wieloma definicjami tych samych funkcji. - Prawdziwym punktem jest to, że można robić różne głupstwa przy pomocy zasad, ale prawie zawsze okazuje się, że nie działają tak dobrze w praktyce jak normalne sposoby. –

3

Cóż, jedną z korzyści wynikających z posiadania kodu w ten sposób jest to, że skraca czas kompilacji.

Powiedzmy masz te pliki na swoim projekcie:

  • ah
  • a.cpp
  • b.cpp

Jeśli masz już a.cpp kompilowane do obiektu plik ao, a następnie dołączenie ah w b.cpp, compilati Powinno być szybsze, ponieważ parser nie będzie musiał zajmować się całą deklaracją/definicją a.

2

Ponieważ nawet w twojej bibliotece DLL inne klasy będą używać twojej klasy. Pliki te muszą zobaczyć deklarację klasy podczas kompilacji, włączając w to .h. Nie mogą widzieć definicji lub będzie wiele definicji funkcji klasowych.

0

W C++ osobna kompilacja modułów kodu (pliki .c lub .cpp) wymagają zdefiniowania prototypów funkcji przed użyciem. Jeśli chcesz użyć klas lub metod zdefiniowanych gdzie indziej, musisz zaimportować pliki .h, aby uzyskać ich definicję. Na koniec linker upewnia się, że wszystkie obietnice zawarte w plikach .h mogą być spełnione przez wszystkie pliki c/cpp.

Umożliwia także tworzenie całych frameworków, takich jak boost tylko poprzez zdefiniowanie plików .h.

+1

Zwiększenie obejmuje głównie pliki nagłówkowe, ponieważ opierają się głównie na szablonach C++ (co oznacza umieszczenie definicji całej klasy w nagłówku, gdy jest na przykład szablonowana). O ile pamiętam, część tej struktury ma plik cpp, który nie opiera się na szablonach C++: na przykład biblioteka wątków. –

2

Twoja edycja ponownie: Boost stanowi ważne wyróżnienie. Klasy szablonów są prawie zawsze definiowane w nagłówkach ze względu na sposób, w jaki kompilator i linker działają w bieżącym standardzie C++. Większość bibliotek szablonów (nie tylko Boost) jest zaimplementowanych w plikach nagłówkowych z tego samego powodu.

5

Zwiększenie nie wstawia całego kodu; wstawia definicje szablonów dla klas, których oczekuje od swoich klientów do utworzenia instancji, takich jak shared_ptr. Wiele bibliotek posiada sekcje, które należy kompilować osobno, takie jak boost :: serialization i program_options.

Inlinowanie może mieć poważne negatywne skutki, ponieważ zwiększa się rozmiar bazy kodu. Zwiększa łączność między twoimi komponentami, nie wspominając już o nukowaniu twojego czasu kompilacji (który to boost z wielu innych powodów :). W efekcie wszystkie twoje jednostki translacyjne miałyby prawie kompletną kopię programu, a niewielka zmiana spowodowałaby, że odbudowałbyś/przetestowałeś wszystko. W przypadku niektórych projektów może to trwać wiele, wiele godzin.

Nigdy nie zauważyłem, że trudniej jest edytować; z mojego doświadczenia wynika, że ​​jest to łatwiejsze z powodu wyraźnego oddzielenia interfejsu od implementacji i wiem, który plik należy znaleźć, aby znaleźć to, czego szukam.

+0

Dobra odpowiedź! Dodam tylko, że niestety rozdzielenie interfejsu i implementacji nie jest tak jasne, jak by sobie tego życzyć, ponieważ część implementacji wycieka do nagłówka w postaci prywatnych pól danych (ponieważ w przeciwnym razie kompilator nie znałby rozmiar klasy). Idiom pimpl służy do rozwiązania tego problemu. –

Powiązane problemy