Usuwam dołączenia w projekcie C ++, nad którym pracuję, i wciąż zastanawiam się, czy powinienem jawnie dołączyć wszystkie nagłówki użyte bezpośrednio w określonym pliku, czy też powinienem zawierać tylko absolutne minimum.
Oto przykład Entity.hpp
:
#include "RenderObject.hpp"
#include "Texture.hpp"
struct Entity {
Texture texture;
RenderObject render();
}
(Załóżmy, że deklaracja przekazania dla RenderObject
nie jest opcją.)
Teraz wiem, że to RenderObject.hpp
obejmuje Texture.hpp
- wiem to, ponieważ każdy RenderObject
ma Texture
członka. Mimo to wyraźnie zawierać Texture.hpp
w Entity.hpp
, bo nie jestem pewien, czy to dobry pomysł, aby polegać na nim są zawarte w RenderObject.hpp
.
Więc: czy to dobra praktyka czy nie?
#ifndef _RENDER_H #define _RENDER_H ... #endif
.#pragma once
się, nie?Odpowiedzi:
Zawsze należy dołączyć wszystkie nagłówki definiujące wszelkie obiekty użyte w pliku .cpp w tym pliku, niezależnie od tego, co wiesz o tym, co jest w tych plikach. Powinieneś mieć osłony we wszystkich plikach nagłówków, aby upewnić się, że wielokrotne dołączanie nagłówków nie ma znaczenia.
Powody:
Texture
obiektami w tym pliku.RenderObject.hpp
tak naprawdę nie potrzebujeTexture.hpp
siebie.Następstwem jest to, że nigdy nie należy umieszczać nagłówka w innym nagłówku, chyba że jest to wyraźnie potrzebne w tym pliku.
źródło
Ogólna ogólna zasada brzmi: dołącz to, czego używasz. Jeśli używasz obiektu bezpośrednio, dołącz bezpośrednio jego plik nagłówka. Jeśli używasz obiektu A, który używa B, ale sam nie używasz B, dodaj tylko Ah
Ponadto, gdy zajmujemy się tym tematem, powinieneś dołączać inne pliki nagłówkowe do pliku nagłówkowego tylko wtedy, gdy faktycznie potrzebujesz go w nagłówku. Jeśli potrzebujesz go tylko w .cpp, umieść go tylko tam: jest to różnica między zależnością publiczną i prywatną i uniemożliwi użytkownikom twojej klasy przeciąganie nagłówków, których tak naprawdę nie potrzebują.
źródło
Tak.
Nigdy nie wiadomo, kiedy te inne nagłówki mogą się zmienić. Na całym świecie sensowne jest umieszczanie w każdej jednostce tłumaczeniowej nagłówków, o których wiesz, że ta jednostka potrzebuje.
Posiadamy osłony nagłówków, aby zapewnić, że podwójne włączenie nie będzie szkodliwe.
źródło
Opinie na ten temat różnią się, ale jestem zdania, że każdy plik (czy to plik źródłowy c / cpp, czy plik nagłówka h / hpp) powinien mieć możliwość samodzielnej kompilacji lub analizy.
W związku z tym wszystkie pliki powinny # zawierać wszystkie potrzebne pliki nagłówkowe - nie należy zakładać, że jeden plik nagłówkowy został już wcześniej dołączony.
To prawdziwy ból, jeśli musisz dodać plik nagłówka i stwierdzić, że używa on elementu zdefiniowanego gdzie indziej, bez bezpośredniego dołączania go ... więc musisz znaleźć (i ewentualnie skończyć z niewłaściwym!)
Z drugiej strony (co do zasady) nie ma znaczenia, jeśli # dołączasz plik, którego nie potrzebujesz ...
Jako osobisty styl układam pliki #include w kolejności alfabetycznej, z podziałem na system i aplikację - pomaga to wzmocnić komunikat „samodzielny i w pełni spójny”.
źródło
Zależy to od tego, czy to przejście przechodnie jest z konieczności (np. Klasa bazowa), czy ze względu na szczegóły implementacji (członek prywatny).
Aby to wyjaśnić, dołączanie przechodnie jest konieczne, gdy można je usunąć dopiero po pierwszej zmianie interfejsów zadeklarowanych w nagłówku pośrednim. Ponieważ jest to już przełomowa zmiana, każdy plik .cpp, który go używa, musi zostać sprawdzony.
Przykład: Ah jest uwzględnione przez Bh, który jest używany przez C.cpp. Jeśli Bh użył Ah dla pewnych szczegółów implementacyjnych, C.cpp nie powinien zakładać, że Bh będzie nadal to robić. Ale jeśli Bh używa Ah dla klasy podstawowej, C.cpp może założyć, że Bh będzie nadal zawierać odpowiednie nagłówki dla swoich klas podstawowych.
Widzisz tutaj faktyczną zaletę NIE powielania inkluzji nagłówka. Powiedz, że klasa podstawowa używana przez Bh tak naprawdę nie należała do Ah i została przekształcona w samą Bh. Bh jest teraz samodzielnym nagłówkiem. Jeśli C.cpp zawiera nadmiarowo Ah, teraz zawiera niepotrzebny nagłówek.
źródło
Może być inny przypadek: masz Ah, Bh i twoje C.cpp, Bh obejmuje Ah
więc w C.cpp możesz pisać
Więc jeśli nie napiszesz tutaj #include „Ah”, co może się zdarzyć? w twoim C.cpp używane są zarówno A, jak i B (np. klasa). Później zmieniłeś kod C.cpp, usunąłeś rzeczy związane z B, ale pozostawiając Bh włączone.
Jeśli uwzględnisz zarówno Ah, jak i Bh, a teraz w tym momencie narzędzia wykrywające niepotrzebne dołączenia mogą pomóc ci wskazać, że uwzględnienie Bh nie jest już potrzebne. Jeśli podasz Bh tylko tak jak powyżej, wtedy ciężko jest narzędziom / ludziom wykryć niepotrzebne dołączenia po zmianie kodu.
źródło
Przyjmuję podobne nieco inne podejście niż proponowane odpowiedzi.
W nagłówkach zawsze uwzględniaj tylko minimum, tylko to, co jest potrzebne, aby kompilacja przebiegła pomyślnie. W miarę możliwości używaj deklaracji przesyłania dalej.
W plikach źródłowych nie ma znaczenia, ile zawierasz. Moje preferencje wciąż muszą zawierać minimum, aby je spełnić.
W przypadku małych projektów, w tym nagłówków tu i tam, nie będzie to miało znaczenia. Ale w przypadku średnich i dużych projektów może to stanowić problem. Nawet jeśli do kompilacji używany jest najnowszy sprzęt, różnica może być zauważalna. Powodem jest to, że kompilator nadal musi otworzyć dołączony nagłówek i parsować go. Tak więc, aby zoptymalizować kompilację, zastosuj powyższą technikę (uwzględnij minimum i użyj deklaracji do przodu).
Chociaż jest to trochę nieaktualne, projekt oprogramowania w dużej skali C ++ (autor: John Lakos) wyjaśnia to wszystko szczegółowo.
źródło
Dobrą praktyką jest nie martwić się o strategię nagłówka, o ile się kompiluje.
Sekcja nagłówka twojego kodu jest tylko blokiem linii, na który nikt nie powinien nawet patrzeć, dopóki nie pojawi się łatwy do rozwiązania błąd kompilacji. Rozumiem pragnienie „poprawnego” stylu, ale tak naprawdę żadnego z tych sposobów nie można opisać jako poprawnego. Dołączenie nagłówka dla każdej klasy może powodować irytujące błędy kompilacji oparte na rozkazie, ale te błędy kompilacji odzwierciedlają również problemy, które może rozwiązać staranne kodowanie (choć prawdopodobnie nie warto tego naprawiać).
I tak, będą mieć te problemy zamówień opartych na raz zaczną się do
friend
ziemi.Możesz pomyśleć o problemie w dwóch przypadkach.
Przypadek 1: Masz niewielką liczbę klas oddziałujących ze sobą, powiedzmy mniej niż tuzin. Regularnie dodajesz te nagłówki, usuwasz je i w inny sposób modyfikujesz, w sposób, który może wpływać na ich wzajemne zależności. Jest to przypadek, który sugeruje twój przykład kodu.
Zestaw nagłówków jest na tyle mały, że rozwiązywanie problemów, które się pojawiają, nie jest skomplikowane. Wszelkie trudne problemy można rozwiązać, przepisując jeden lub dwa nagłówki. Martwienie się o strategię nagłówka polega na rozwiązywaniu problemów, które nie istnieją.
Przypadek 2: Masz dziesiątki klas. Niektóre klasy stanowią kręgosłup twojego programu, a przepisanie ich nagłówków zmusiłoby cię do przepisania / rekompilacji dużej części twojej bazy kodu. Inne klasy używają tego kręgosłupa do osiągania różnych celów. Jest to typowe ustawienie biznesowe. Nagłówki są rozmieszczone w katalogach i nie można realistycznie zapamiętać nazw wszystkiego.
Rozwiązanie: W tym momencie musisz pomyśleć o swoich klasach w logicznych grupach i zebrać te grupy w nagłówki, które powstrzymają cię od ciągłego
#include
powtarzania. To nie tylko ułatwia życie, ale jest także niezbędnym krokiem do korzystania ze wstępnie skompilowanych nagłówków .Skończyć
#include
ing zajęcia nie są potrzebne, ale kogo to obchodzi ?W takim przypadku Twój kod wyglądałby jak ...
źródło