Widziałem, że w C ++ istnieje kilka różnych paradygmatów dotyczących tego, co wchodzi do pliku nagłówkowego, a co do pliku CPP. AFAIK, większość ludzi, szczególnie tych z pochodzenia C, wykonuje:
foo.h
class foo {
private:
int mem;
int bar();
public:
foo();
foo(const foo&);
foo& operator=(foo);
~foo();
}
foo.cpp
#include foo.h
foo::bar() { return mem; }
foo::foo() { mem = 42; }
foo::foo(const foo& f) { mem = f.mem; }
foo::operator=(foo f) { mem = f.mem; }
foo::~foo() {}
int main(int argc, char *argv[]) { foo f; }
Jednak moi wykładowcy zwykle uczą języka C ++ dla początkujących:
foo.h
class foo {
private:
int mem;
int bar() { return mem; }
public:
foo() { mem = 42; }
foo(const foo& f) { mem = f.mem; }
foo& operator=(foo f) { mem = f.mem; }
~foo() {}
}
foo.cpp
#include foo.h
int main(int argc, char* argv[]) { foo f; }
// other global helper functions, DLL exports, and whatnot
Pochodzę z Javy i zawsze trzymałem się tej drugiej drogi z kilku powodów, takich jak to, że muszę coś zmienić tylko w jednym miejscu, jeśli zmieni się nazwa interfejsu lub metody, że podoba mi się różne wcięcie rzeczy w klasach, gdy spójrz na ich implementację i że uważam, że nazwy są bardziej czytelne w foo
porównaniu do foo::foo
.
Chcę zbierać argumenty za i przeciw dla obu stron. Może są jeszcze inne sposoby?
Wadą mojego sposobu jest oczywiście potrzeba okazjonalnych deklaracji forward.
źródło
foo.cpp
teraz nie ma nic wspólnego z twojąfoo
klasą i powinno pozostać puste (być może po#include
to, aby twój system budowania był szczęśliwy).Odpowiedzi:
Podczas gdy druga wersja jest łatwiejsza do napisania, łączy interfejs z implementacją.
Pliki źródłowe zawierające pliki nagłówkowe należy rekompilować za każdym razem, gdy pliki nagłówkowe są zmieniane. W pierwszej wersji plik nagłówka byłby zmieniany tylko wtedy, gdy trzeba zmienić interfejs. W drugiej wersji zmieniłbyś plik nagłówka, jeśli potrzebujesz zmienić interfejs lub implementację.
Poza tym nie powinieneś ujawniać szczegółów implementacji , otrzymasz niepotrzebną rekompilację z drugą wersją.
źródło
Zrobiłem to drugi raz w latach 93–95. Minęło kilka minut, aby ponownie skompilować małą aplikację z 5-10 funkcjami / plikami (na tym samym komputerze 486 .. i nie, nie wiedziałem też o klasach, miałem zaledwie 14-15 lat i nie było internetu ) .
Tak więc to, czego uczysz początkujących i czego używasz zawodowo, to bardzo różne techniki, szczególnie w C ++.
Myślę, że porównanie między C ++ i bolidem F1 jest trafne. Nie umieszczasz początkujących w samochodzie F1 (który nawet się nie uruchomi, dopóki nie podgrzejesz silnika do 80-95 stopni Celsjusza).
Nie ucz C ++ jako pierwszego języka. Powinieneś być wystarczająco doświadczony, aby wiedzieć, dlaczego opcja 2 jest gorsza niż opcja 1 w ogóle, wiedzieć trochę, co oznacza kompilacja statyczna / linkowanie, a tym samym zrozumieć, dlaczego C ++ preferuje ją po raz pierwszy.
źródło
Druga metoda jest tym, co nazwałbym klasą całkowicie inline. Piszesz definicję klasy, ale cały kod, który z niej korzysta, po prostu wstawi kod.
Tak, kompilator decyduje, kiedy inline, a kiedy nie ... W tym przypadku pomagasz kompilatorowi w podjęciu decyzji i potencjalnie skończy się to generowaniem mniejszego kodu i potencjalnie szybszym.
Ta przewaga prawdopodobnie przeważy nad faktem, że jeśli zmodyfikujesz implementację funkcji, musisz odbudować wszystkie źródła, które z niej korzystają. W lekkiej naturze klasy nie będziesz modyfikował implementacji. Jeśli dodasz nową metodę, i tak będziesz musiał zmodyfikować nagłówek.
Ponieważ Twoja klasa staje się coraz bardziej złożona, nawet dodając pętlę, korzyść z robienia tego w ten sposób spada.
Nadal ma swoje zalety, w szczególności:
Minusem inliningu staje się problem, gdy oznacza to, że musisz wprowadzić specyfikacje implementacji do swojego nagłówka, tj. Musisz zacząć włączać dodatkowe nagłówki.
Pamiętaj, że szablony są szczególnym przypadkiem, ponieważ prawie musisz podać szczegóły implementacji. Możesz ukryć go w innym pliku, ale musi tam być. (Istnieje wyjątek od tej reguły z instancjami, ale ogólnie wstawiasz szablony).
źródło
Może to nie być znaczące lub prawdziwe, jeśli plik wykonywalny staje się większy, ale więcej kodu w plikach nagłówkowych pozwala kompilatorowi na zoptymalizowanie szybkości.
Jeśli decydujesz, czy napisać bibliotekę tylko nagłówkową , ten temat jest tylko jednym z twoich problemów.
źródło