• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Pytanka odnośnie kompilacji i linkowania

Object Storage Arubacloud
0 głosów
439 wizyt
pytanie zadane 22 czerwca 2017 w C i C++ przez niezalogowany
Programuję już półtorej roku, a nadal nie rozumiem do końca pewnych rzeczy związanych z kompilacją i linkowaniem. Ostatnio bawię się też szablonami, więc coś postanowiłem się zapytać o kilka kwestii.

1. Dlaczego załączamy plik .h w mainie i pliku .cpp, a nie plik .cpp w pliku .h i .h w mainie? Przecież w ten sposób załączamy do pliku z gotowymi definicjami plik z samymi deklaracjami. Skoro mamy całe definicje to po co załączać jeszcze deklaracje?

2. Jeśli chodzi o szablony to nie chciało tak działać, nawet czytałem gdzieś dlaczego (chociaż już zapomniałem). Więc spróbowałem sposobu takiego

w mainie includuję plik .h

w pliku .h includuję plik .cpp

i voila, działa!

Nie byłem pewien czy nie jest to zła praktyka koderska, ale gdzieś przeczytałem, że tak właśnie z szablonami się robi i nazywa się to modelem włączania. Jest to nieoficjalne rozwiązanie bo standard C++11 nie określił chyba jeszcze dokładnie kwestii szablonów. Jednak ten sposób działa tylko dla szablonów, a dla zwykłych funkcji nie bo od razu są błędy kompilacji.

Jako, że tworzę własną bibliotekę alghorithms, w której chcę mieć zarówno zwykłe funkcje jak i szablony i chcę mieć deklaracje i definicje w oddzielnych plikach to zamiast includować plik .cpp ja includuję plik .def albo .aaa albo .xxx w pliku .h, a plik .h w mainie i wtedy wszystko działa. Czy jest to dobry pomysł, czy z jakiegoś powodu zły?

3. Czemu z rozmieszczeniem szablonów w plikach musi być tak niestandardowo?

4. Wyjaśniłby ktoś w miarę dokładnie i zarazem łopatologicznie jak to jest z tą kompilacją i linkowaniem? Jak to przebiega ogólnie, na co uważać etc?

3 odpowiedzi

+3 głosów
odpowiedź 22 czerwca 2017 przez adrian17 Ekspert (344,860 p.)

Zaczynając: .h i .cpp to rozszerzenia. .cpp są kompilowane. Kiedy dołączasz coś przez #include, nie ma znaczenia, czy to .h, czy .cpp. #include to dosłownie copy-paste. To tak jakbyś zawartość A wkleił w miejscu #include <A>.

Przecież w ten sposób załączamy do pliku z gotowymi definicjami plik z samymi deklaracjami

Co do samych funkcji, zgadza się, definicja funkcji nie potrzebuje deklaracji. Ale już taka definicja metody klasy potrzebuje definicję klasy, która zazwyczaj jest w nagłówku. Ogólnie zależności są na tyle złożone, że zawsze w A.cpp  powinno się include'ować A.h, bo w 99% kod bez tego się nie skompiluje, a w pozostałym 1% to wciąż może doprowadzić do konfuzji w przyszłości.

Więc spróbowałem sposobu takiego

w mainie includuję plik .h

w pliku .h includuję plik .cpp

i voila, działa!
 

Twój plik .cpp (poza byciem niezależnie kompilowanym) z punktu widzenia main.cpp jest tylko kolejnym nagłówkiem. Rezultat taki jak gdybyś wrzucił kod szablonu do .h, tylko gorzej, bo jest dodatkowo kompilowany i robi się dziwnie. Ogólnie sprowadza się to do tego że

  • nie include'uj .cpp
  • nie wrzucaj implementacji szablonów "nagłówkowych" do .cpp

Czemu z rozmieszczeniem szablonów w plikach musi być tak niestandardowo?

Nie rozumiem pytania.

Wyjaśniłby ktoś w miarę dokładnie i zarazem łopatologicznie jak to jest z tą kompilacją i linkowaniem?

Wpisz "compiler linker" w google images. Obrazek mówi więcej niż tysiąc słów. Na przykład https://devblogs.nvidia.com/wp-content/uploads/2014/04/separate_compilation_linking.png . Jak nadal masz pytania, to pytaj.

komentarz 22 czerwca 2017 przez niezalogowany

Z tymi szablonami to chodzi mi o to, że nie wiem czemu w przeciwieństwie do zwykłych funkcji nie można zrobić nagłówków w .h a definicji w .cpp.

Twój plik .cpp (poza byciem niezależnie kompilowanym) z punktu widzenia main.cpp jest tylko kolejnym nagłówkiem. Rezultat taki jak gdybyś wrzucił kod szablonu do .h, tylko gorzej, bo jest dodatkowo kompilowany i robi się dziwnie. 

Czyli takie coś

#ifndef COS_H
#define COS_H

#include <iostream>
pozostałe biblioteki

using namespace std;

template <class typ>
int fun_szablonowa( argumenty );

int zwykla_fun( argumenty );

...

include "cos.cpp"


#endif

powinno zadziałać tak samo jak takie coś

#ifndef COS_H
#define COS_H

#include <iostream>
pozostałe biblioteki

using namespace std;

// HEADERS

template <class typ>
int czyBylaWylosowana( argumenty );

int zwykla_fun( argumenty );

...

// SOURCES

tutaj definicje

#endif

Dobrze to zrozumiałem? O co chodzi, że robi się dziwnie jak załączę plik .cpp?

komentarz 22 czerwca 2017 przez adrian17 Ekspert (344,860 p.)

Z tymi szablonami to chodzi mi o to, że nie wiem czemu w przeciwieństwie do zwykłych funkcji nie można zrobić nagłówków w .h a definicji w .cpp.

Szablon nie istnieje jako prawdziwy kod, póki nie zostanie użyty. (można próbować to porównać do projektu samochodu, który nie został jeszcze wyprodukowany, ale to naciągane) Kompilacja zamienia kod C++ na prawie-wykonywalny (patrz linker i pliki .o) kod binarny. Próba skompilowania pliku .cpp z implementacją szablonu da... nic, bo nigdzie nie został użyty. Gdzie jest informacja, że np. potrzebne jest użycie szablonu czyBylaWylosowana<double>? W miejscu gdzie jest używany, czyli np. main.cpp. Dlatego implementacja szablonu ma być widoczna z miejsca użycia - by można było skonkretyzować szablon dla tego konkretnego używanego typu.

Mam nadzieję że to ma jakiś sens.

 O co chodzi, że robi się dziwnie jak załączę plik .cpp?

Powtórzę: to robi dokładnie to samo, co gdybyś dołączył plik .cpp, .h, .asdf z tym samym tekstem. Różnica jest taka, że ten .cpp jest dodatkowo niezależnie kompilowany, co nie ma sensu, niczemu nie służy i może potem dodać jeszcze błędy przy linkowaniu.

komentarz 22 czerwca 2017 przez niezalogowany
Dzięki, teraz czaję to z szablonami.

Piszesz, że tylko plik .cpp jest dodatkowo niezależnie kompilowany? Czyli jak rozdzielę na plik .h i .def ( a nie .cpp ) to będzie ok? Czy też jest to ryzykowne lub z jakiegoś powodu nieopłacalne?

A ta druga wersja, gdzie mam w jednym pliku .h najpierw deklaracje, a potem definicje jest poprawna? Bo coś mi mówi, że ma to taki sam efekt jak gdybym miał same definicje, a na kompilację nie ma wpływu, więc wszystko jest ok? Pewnie ktoś spyta, po co mi w jednym pliku najpierw deklaracje skoro kawałek dalej mam całe definicje. Otóż chodzi o przejrzystość kodu, żebym nawet dla funkcji szablonowych mógł oddzielnie przeglądać nagłówki i definicje.
komentarz 22 czerwca 2017 przez adrian17 Ekspert (344,860 p.)

Tu jeszcze szybki obrazek powyższego:

http://puu.sh/wrDbb/94a3e9a7fe.png

Bo coś mi mówi, że ma to taki sam efekt jak gdybym miał same definicje, a na kompilację nie ma wpływu, więc wszystko jest ok?

Osobne deklaracje są potrzebne w zasadzie tylko, gdy musisz skorzystać z zadeklarowanej rzeczy nad definicją. Dla szablonów jest to dość rzadkie i w praktyce nikt nie robi osobnych deklaracji i definicji szablonów, jeśli nie ma ku temu konkretnej potrzeby.

komentarz 22 czerwca 2017 przez niezalogowany
edycja 22 czerwca 2017
Ten obrazek mówi wszystko.

Ale osobne deklaracje sprawiają, że można sobie wygodnie przeglądać nagłówki z komentarzami jak spis treści, zamiast przewijać długie definicje. No i chcę mieć w jednej bibliotece zarówno zwykłe funkcje jak i szablonowe. I tylko po to mi potrzebne te dodatkowe deklaracje. Nie jest to chyba błąd?
+1 głos
odpowiedź 22 czerwca 2017 przez Buby Pasjonat (19,590 p.)

Jako, że wcześniejsze zagadnienia wyjaśnili koledzy, to umieszczam tylko przydatne linki do procesu kompilacji.

Kompilacja to dość złożony proces, jednak uważam, że artykuł jak i video doskonale go opisują. [Polecam zacząć od artykułu, bo wtedy sposób omawiania na filmie staje się dość klarowny]

Artykuł nt. proces kompilacji w GCC

Video dot procesu kompilacji w C/C++

 

0 głosów
odpowiedź 22 czerwca 2017 przez j23 Mędrzec (194,920 p.)
  1. Dlatego, że plików źródłowych (.cpp) się nie załącza w plikach nagłówkowych (.h). Pliki źródłowe powinny być kompilowane tylko raz.
  2. definicje metod szablonowych (podobnie jak funkcje inline) muszą być widoczne przez kompilator w miejscu konkretyzacji szablonu, dlatego cały szablon musi być w pliku nagłówkowym. Wyjątkiem są pełne specjalizacje funkcji/metod/klas.
  3. patrz 2.

Podobne pytania

0 głosów
1 odpowiedź 211 wizyt
–1 głos
1 odpowiedź 277 wizyt
pytanie zadane 17 czerwca 2016 w Sieci komputerowe, internet przez yDynBach Początkujący (370 p.)
0 głosów
2 odpowiedzi 214 wizyt

92,572 zapytań

141,423 odpowiedzi

319,645 komentarzy

61,959 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...