Co zrobić, jeśli nienawidzę plików nagłówkowych C ++?

25

Zawsze miałem wątpliwości co do plików nagłówkowych. Są tak dziwne: dołączasz plik .h, który nie zawiera .cpp, ale .cpp też jest jakoś skompilowany.

Ostatnio dołączyłem do projektu zespołowego i oczywiście używane są zarówno pliki .h, jak i .cpp.
Rozumiem, że jest to bardzo ważne, ale nie mogę żyć z kopiowaniem i wklejaniem każdej deklaracji funkcji w każdej z wielu klas, które mamy.

Jak skutecznie obsługiwać konwencję 2-plików?
Czy są jakieś narzędzia, które mogą w tym pomóc, lub automatycznie zmienić jeden plik, który wygląda jak przykład poniżej, na .h i .cpp? (specjalnie dla MS VC ++ 2010)

class A
{
...
    Type f(Type a,Type b)
    {
        //implementation here, not in another file!
    }
...
};

Type f(Type a)
{
     //implementation here
}
...
Oleh Prypin
źródło
8
To pytanie może przebiegać na kilka sposobów ... „Dlaczego potrzebujemy nagłówków podczas używania c ++” lub „Czy uważasz, że nowoczesny język, który ma być skompilowany, powinien używać nagłówków?” Na razie ma w tytule „Co mam robić” i „Nienawidzę”, co wywołuje mnóstwo flag.
Tim Post
4
Twoje pytanie sprawia wrażenie, jakbyś nie rozumiał C ++ lub jak kompiluje go dowolny używany system. Naucz się go używać poprawnie, a następnie zadawaj bardziej subiektywne pytania.
David Thornley,
31
Twoje pierwsze zdanie wskazuje, że „nie rozumiesz wszystkiego w nagłówkach”. Dołączenie pliku .h nie powoduje, że odpowiedni plik .cpp jest „jakoś skompilowany”. Pliki .cpp kompilujesz samodzielnie. Jeśli nie skompilowałeś odpowiedniego pliku .cpp, wówczas dołączenie nagłówka bez odpowiedniego pliku obiektowego spowoduje niepowodzenie konsolidatora.
Paul Butcher,
5
Co robić? Znajdź inny język, jeśli tak bardzo Cię to denerwuje.
Paul Nathan
5
O „nie można żyć z kopiowaniem i wklejaniem”: za każdym razem, gdy aktualizuje się funkcję, należy aktualizować wszystkie miejsca, w których jest ona wywoływana. Ponieważ osoby dzwoniące są znacznie trudniejsze do znalezienia niż deklaracja w pliku nagłówka, aktualizacja nagłówka jest tylko drobnym szczegółem.
Sjoerd,

Odpowiedzi:

15

Pisanie bardziej przyjazne dla refaktoryzacji C ++

W C ++ nie musisz wcale używać nagłówków. Możesz zdefiniować cały obiekt w jednym pliku, tak jak w C # lub Java. Programiści C zwykle przechowują tylko połączenia zewnętrzne w pliku nagłówkowym. Wszystkie wywołania wewnętrzne byłyby zdefiniowane w pliku .c. Tym samym tokenem możesz zarezerwować swoje pliki C ++ .h dla klas / interfejsów (czysto wirtualne klasy abstrakcyjne) / etc. które mają być udostępniane poza biblioteką DLL. W przypadku klas wewnętrznych / struktur / interfejsów itp. Wystarczy dołączyć potrzebny plik .cpp:

#include<myclass.cpp>

Nie wydaje się to być najpopularniejszym podejściem, ale jest to legalne C ++. Z pewnością byłaby to możliwość dla całego twojego wewnętrznego kodu. Pozwala to wewnętrznemu kodowi i zestawowi klas na znacznie bardziej radykalną zmianę, zapewniając jednocześnie bardziej stabilny interfejs dla kodu poza biblioteką / plikiem wykonywalnym do interakcji.

Umieszczenie całej klasy w jednym pliku ułatwi robienie tego, co chcesz. Nie rozwiąże problemu zmiany nazwy metody i konieczności przeszukiwania każdego miejsca, w którym ta metoda jest wywoływana, ale zapewni bardziej zrozumiałe komunikaty o błędach. Nie ma nic gorszego niż to, że nagłówek deklaruje metodę w jedną stronę, ale implementujesz ją inaczej. Inny kod, który wywołuje plik nagłówkowy, zostanie poprawnie skompilowany i otrzymasz wyjątek łącza, podczas gdy plik implementacyjny będzie tym, który skarży się, że metoda nie została zdefiniowana. Kiedy zdefiniujesz każdą metodę na miejscu (w rzeczywistej deklaracji klasy), otrzymasz ten sam komunikat o błędzie bez względu na to, jaki plik ją zawiera.

Możesz także przyjrzeć się temu pytaniu: Dobre narzędzia refaktoryzujące dla C ++

Jak C / C ++ rozwiązuje pliki nagłówkowe / implementacyjne

Na podstawowym poziomie C (i C ++ jest zbudowany na tej podstawie) pliki nagłówkowe deklarują obietnicę funkcji / struct / zmiennej, która wystarcza, aby kompilator mógł utworzyć plik obiektowy. Podobnie pliki nagłówkowe C ++ deklarują obietnicę funkcji, struktur, klas itp. Jest to definicja, której używa kompilator do rezerwowania miejsca na stosie itp.

Pliki .c lub .cpp mają implementację. Gdy kompilator konwertuje każdy plik implementacji na plik obiektowy, istnieją niedostosowania do niezaimplementowanych pojęć (co zadeklarowano w nagłówku). Linker łączy zaczepy z implementacjami w innych plikach obiektowych i tworzy większy plik binarny, który zawiera cały kod (biblioteka współdzielona lub plik wykonywalny).

VS Specific

Jeśli chodzi o pracę z tymi w Visual Studio, istnieją pewne kreatory, które pomagają nieco to ułatwić. Kreator nowej klasy utworzy pasującą parę plików nagłówkowych i implementacyjnych. Istnieje nawet funkcja przeglądarki klasy, która pozwoli Ci zadeklarować nowe metody. Wprowadzi definicję do nagłówka i kod źródłowy implementacji w pliku .cpp. Visual Studio ma te funkcje od ponad dekady (o ile ich używałem).

Berin Loritsch
źródło
Problem polega na tym, że cały czas mocno modyfikuję klasy, nie tylko dodając nowe funkcje itp.
Oleh Prypin 10.01.11
5
@BlaXpirit: Dlaczego więc ciągle modyfikujesz klasy? Jednym z pomysłów stojących za projektem OO jest posiadanie wielu dość stabilnych elementów składowych. Gdybym mocno modyfikował klasy, chciałbym bardziej dynamiczny język, taki jak Common Lisp lub Python.
David Thornley,
2
To jest to co robię. Poprawiam / modyfikuję „bloki konstrukcyjne” i
dodaję
C ++ nigdy nie był przyjazny dla refaktorów. Koncepcja refaktoryzacji nie nabrała rozpędu, dopóki nie istniały narzędzia, które naprawdę ułatwiłyby jej tworzenie IDE Java. UWAGA: te funkcje były dostępne dla programistów Smalltalk i innych języków, ale nie stały się one popularne, dopóki nie były dostępne dla wielu osób. Do tej pory nie widziałem, żeby ktoś inteligentnie zaimplementował to w C ++. Być może Resharper z JetBrains? Wiem, że robi kod C # i VB, ale nie jestem pewien, czy da ci to refaktoryzację C ++.
Berin Loritsch,
@Berin: Szukałem narzędzi do refaktoryzacji C ++ rok lub dwa lata temu i znalazłem dwie rzeczy. Były wtedy dość drogie i nie widziałem wersji próbnych, więc nie wiem, co zrobili. Co więcej, działaliśmy tylko z emacsem, co ograniczałoby jego skuteczność w sklepie Visual Studio.
David Thornley,
13

Zostań programistą Java.

Jeśli naprawdę musisz kontynuować programowanie w C ++, możesz spróbować użyć IDE. Często oferują pewien mechanizm, za pomocą którego można dodać metodę do klasy, i to automatycznie umieszcza deklarację w pliku .h, a definicję w pliku .cpp.

Paul Butcher
źródło
2
kthx, ja w pewnym sensie znam Javę, ale nie można z niej tworzyć bibliotek DLL DLL niskiego poziomu, prawda?
Oleh Prypin
41
Nie wiem dlaczego, ale „Zostań programistą Java” brzmi jak obelga: D.
Oliver Weiler
2
Jeśli chcesz grać na niskim poziomie, zapomnij o „łatwych językach”. Niski poziom kosztuje pot i łzy.
Batibix,
5
Niezbyt pomocna odpowiedź.
ChrisF
1
@OliverWeiler Nie postrzegam „zostania programistą Java” jako zniewagą. Programuję zarówno w C ++, jak i Javie, ale zdecydowanie wolę Javę, ponieważ o wiele łatwiej jest usiąść i wykurzyć kod, który działa (i jest bardziej przenośny). Jeśli z jakiegoś powodu nie znosisz plików nagłówkowych, wypróbowanie Javy może być właściwym wyborem (choć dziwne, że nie znosisz plików nagłówkowych; rozważę zmianę IDE).
Trixie Wolf
7

Możesz być zainteresowany programem makeheaders od Hwaci (tych, którzy robią SQLite i Fossil).

Zobacz także, jak zbudowana jest skamielina, aby mieć pomysł.

Benoit
źródło
5
Pytający nadal musi całkiem dobrze rozumieć związek między .h i .cpp.
Job
2
Rozumiem podstawy. Odpowiedź wydaje się być dokładnie tym, czego potrzebuję.
Oleh Prypin
4

Kiedy piszesz pierwsze wiersze nowej klasy, zwykle dzieje się tak, ponieważ potrzebujesz go tylko w jednym miejscu w tym czasie. Później może być używany w większej liczbie miejsc, ale początkowo zwykle nie jest.

Wiele moich zajęć zaczyna się na początku bieżącego pliku .cpp. Kiedy ustabilizuje się na tyle, że można go używać w wielu miejscach, wycinam i wklejam go do nagłówka. Chociaż często klasa znika tak szybko, jak się pojawiła.

Sjoerd
źródło
-1

Jako sugestię dotyczącą obsługi plików nagłówkowych C ++ często stosuje się je bez rozszerzenia pliku lub sufiksu, tak jak w przypadku bibliotek „GCC”.

W takim przypadku sugeruję użycie rozszerzenia lub sufiksu pliku „ .hpp” (lub co najmniej „ .hxx”).

Może być konieczne skonfigurowanie kompilatora, środowiska programisty lub programu wbudowanego.

umlcat
źródło
3
Czy mówisz o tym, jak dołączyć taki plik #include <iostream>? To nie są tylko dla biblioteki GCC. W rzeczywistości jest on zdefiniowany w standardzie C ++ z 1997 r. , Sekcja 17.3.1.2. Unikałbym nazywania takich plików. Możesz, ale powodem, dla którego zrobiła to standardowa biblioteka C ++, było prawdopodobnie uniknięcie konfliktów nazw. Wydaje mi się to naprawdę dziwne, gdy kompilatory automatycznie dodają „.h”, kiedy dołączasz nagłówek, wydaje mi się to dość niestandardowe. I nigdy nie widzę nagłówków nazw bez sufiksu, z wyjątkiem standardowej biblioteki c ++.
wedyjość
1
Powinienem też zauważyć, że wszystkie kompilatory, których użyłem, z wyjątkiem Borlanda (którego tak bardzo nienawidzę), nie dodają automatycznie „.h” lub „.hpp” lub „.hxx” podczas próby dołączyć plik bez przyrostka. Nie oczekuj, #include <someclass>że będziesz czytany jak #include <someclass.hpp>we wszystkich kompilatorach. Twój kod się zepsuje.
wedyjość