Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?
Autor: Piotr Szawdyński
Późniejsze modyfikacje: 'Dante'
Kurs C++

Dzielenie kodu na kilka plików źródłowych

[lekcja] Dokument opisuje jak się dzieli kod źródłowy na kilka plików.

Poznajemy dyrektywę #include na nowo

Wraz z rozwojem każdego programu kodu przybywa, a poruszanie się po nim staje się coraz bardziej uciążliwe ze względu na jego długość. Z pewnością starasz się grupować tematycznie większość funkcji w programie, jednak i to niewiele daje, gdy przychodzi pracować z kodem, który ma co najmniej 1000 wierszy. Z pomocą przychodzi tu dyrektywa #include, którą już miałeś okazję niejednokrotnie poznać, stosując ją nawet do najprostszego programu.
Język C++ to zbiór logicznych zasad umożliwiających programowanie. Same zasady umożliwiają zarządzać danymi, jednak nie są one wystarczające do tego, aby wykorzystywać w łatwy sposób możliwości sprzętowe komputera. Ponieważ język C++ umożliwia łatwe organizowanie danych, to oczywistym też jest, że równie potrzebnym elementem jest interfejs, umożliwiający prezentację danych oraz interfejs reagujący na wszystkie urządzenia, jakie posiadamy w komputerze (np. mysz, klawiaturę, kartę graficzną, kartę sieciową itd.).
Zadaniem dyrektywy #include jest umożliwienie łatwego wykorzystywania zasobów sprzętowych komputera, poprzez dołączanie plików nagłówkowych bibliotek, które są odpowiedzialne za komunikację z różnymi urządzeniami. Innymi słowy za każdym razem, gdy korzystałeś z dyrektywy #include, dołączałeś do swojego programu interfejs umożliwiający łatwy dostęp do wybranych zasobów komputera. Za pomocą tego polecenia będziesz mógł w łatwy sposób pisać serwery TCP/UDP, używać OpenGL'a, czy też innych modułów umożliwiających łatwy dostęp do sprzętowych zasobów komputera.
Niniejsza dyrektywa pomimo, iż jest tak prosta w użyciu jest potężnym narzędziem w ręku programisty. Oprócz dołączania istniejących bibliotek, pozwala Ci ona dołączać własne biblioteki, które wkrótce sam zaczniesz pisać. Dzięki tej własności będziesz mógł zorganizować swój kod źródłowy lepiej, a wszelkie modyfikacje kodu staną się szybsze i łatwiejsze.

Ustawianie ścieżki do pliku nagłówkowego

Do tej pory gdy chciałeś dołączyć bibliotekę do programu, stosowałeś tylko i wyłącznie zapis #include <ścieżka do pliku>. Użycie ostrych nawiasów <...> informuje kompilator, aby przeszukiwał domyślne ścieżki, w których znajdują się pliki nagłówkowe. Ścieżki te są ustawione w środowisku Code::Blocks, które wskazują na katalogi w których znajdują się wszystkie standardowe biblioteki. Jeśli chcesz sprawdzić jakie ścieżki są domyślnie dołączane podczas kompilacji programów wejdź w następujące ustawienia:
  • wybierz: Narzędzia/Opcje kompilatora
  • kliknij zakładkę: Katalogi
  • w ramce która się pokazała, kliknij Pliki nagłówkowe C++
Lista którą widzisz, to zbiór katalogów, które są przeszukiwane w celu odnalezienia odpowiedniego pliku nagłówkowego. Jeśli kompilator nie odnajdzie żądanego pliku nagłówkowego, kompilator zwróci błąd informując Cię, że pliku o podanej nazwie nie znaleziono.
Drugą metodą na dołączanie plików nagłówkowych jest wykorzystanie zapisu #include "ścieżka do pliku". Zapis z użyciem podwójnych apostrofów informuje kompilator, że plik nagłówkowy ma być poszukiwany tylko i wyłącznie względem aktualnego katalogu, w którym znajduje się nasz projekt.

Rozszerzenia plików i ich znaczenie

Na przestrzeni lat język C, a od jakiegoś czasu również i C++ wypracowały sobie nazwy rozszerzeń dla plików, które automatycznie sugerują co się w nich znajduje i w jakim standardzie były pisane.

Pliki *.h *.hpp

Pliki *.h i *.hpp, są nazywane plikami nagłówkowymi (ang. header files). Pierwszy z nich, tj. *.h oznacza, że był on pisany zgodnie ze standardami języka C. Rozszerzenie *.hpp natomiast mówi nam, że program pisany był zgodnie ze standardami języka C++. Jeśli masz kompilator C++ to nie musisz obawiać się o problemy z wykorzystywaniem bibliotek pisanych zarówno w C jak i C++, ponieważ standard C++ powstał w oparciu o C. Problemy możesz mieć natomiast w sytuacji odwrotnej, ponieważ mogą być zastosowane polecenia których język C po prostu nie zna.
Każdy plik nagłówkowy powinien zawierać tylko i wyłącznie interfejs. Przez słowo interfejs rozumiemy:
  • deklaracje typów
  • deklaracje funkcji
  • deklaracje struktur
  • deklaracje klas
  • deklarację ewentualnych zmiennych globalnych
Dodatkowo dołączamy do niego niezbędne pliki nagłówkowe, jakie będą wykorzystywane przez daną bibliotekę. W pliku nagłówkowym nie umieszczamy natomiast bloków funkcji. Można powiedzieć po prostu, że w pliku nagłówkowym umieszczamy wszystko oprócz bloków funkcji.

Pliki *.c *.cpp

Pliki *.c i *.cpp nazywamy plikami źródłowymi. Jak nietrudno domyślić się, *.c oznacza standard użytego języka C, natomiast *.cpp standard użytego języka C++. W plikach z takim rozszerzeniem umieszczamy tylko i wyłącznie definicje funkcji, czyli nazwę funkcji razem z jej ciałem (czyli blokiem funkcji).

Inne rozszerzenia plików

Język C++ nie narzuca nazw dla rozszerzeń plików. Zalecane jest jednak stosowanie się do wymienionych standardów, ponieważ są one jasne i zrozumiałe przez wszystkich programistów C i C++. Dodatkowo edytory często rozpoznają typ pliku po rozszerzeniach i w zależności od nich kolorują Tobie składnię.

Budowa pliku nagłówkowego *.h *.hpp

Istnieje co najmniej kilka wersji budowy plików nagłówkowych dla języka C i C++. Niektóre z nich nie działają jednak pod wszystkimi kompilatorami, dlatego też skupię się tylko i wyłącznie na jednej, która jest akceptowana przez wszystkie kompilatory.
C/C++
#ifndef nazwaPliku_hpp
#define nazwaPliku_hpp
/*
  tutaj piszesz cały interfejs
*/
#endif
Opis użytych instrukcji preprocesora:
#ifndef zmienna_preprocesoraInstrukcja preprocesora, która sprawdza czy zmienna preprocesora o podanej nazwie istnieje. Jeśli zmienna preprocesora nie istnieje to wszystkie instrukcje, które się znajdują poniżej #ifndef, zostaną wykonane. Jeśli natomiast zmienna będzie istniała, kompilator pominie wszystkie instrukcje jakie znajdują się pomiędzy słowami preprocesora #ifndef, a #endif.
endifinstrukcja oznacza miejsce końca bloku warunkowego preprocesora.
#define nazwa_zmiennejPolecenie służy do tworzenia zmiennych preprocesora.
Zaprezentowany przykład czytasz następująco:
#ifndef dowolna_nazwajeśli nie istnieje dowolna_nazwa, wykonuj blok
#define dowolna_nazwautwórz zmienną o nazwie dowolna_nazwa
#endifkoniec bloku warunkowego
Wykorzystując instrukcje preprocesora zabezpieczasz bibliotekę przed wielokrotnym dołączaniem tego samego kodu do własnego programu. Jeśli go nie użyjesz, a dołączysz tą samą bibliotekę co najmniej dwukrotnie w programie (nawet w różnych plikach), otrzymasz błąd kompilacji nawet jeśli wszystko będzie poprawnie napisane. Nazwy zmiennych, które definiujesz za pomocą preprocesora muszą być unikatowe podczas kompilacji projektu dla każdego używanego pliku, tak więc w każdym pliku musi się znajdować inna nazwa zmiennej preprocesora. Najpopularniejszą metodą zapewnienia sobie unikatowych nazw zmiennych, jest używanie nazwy pliku dla zmiennej preprocesora. Nie jest to jednak obowiązek i masz tu praktycznie pełną dowolność. Nazwy zmiennych preprocesora mają takie same kryteria dla nazewnictwa jak zmienne języka C++.

Budowa pliku źródłowego *.c *.cpp

Plik źródłowy ma bardzo prostą budowę. Jedyne co musisz zrobić to dołączyć plik nagłówkowy pliku, który opisuje interfejs jaki znajduje się w tym pliku.
C/C++
#include "nazwaPliku.hpp"
/*
tutaj piszesz definicje funkcji
*/

Błędy kompilacji

Jeśli będziesz próbował skompilować plik *.cpp i nie będzie w nim żadnych błędów, otrzymasz następujący błąd kompilacji:
[Linker error] undefined reference to `WinMain@16'
ld returned 1 exit status
Komunikat ten informuje Cię, że w programie nie ma 'ciała' programu, czyli głównej funkcji programu. Ponieważ pliki, które utworzyłeś nie mają być programem, tylko źródłem dołączanym do programu, który będzie zawierał funkcję main(), wskazane jest aby ten plik nie zawierał funkcji o którą 'doczepił się' kompilator.
Dołączając natomiast plik nagłówkowy (*.hpp) do programu głównego za pomocą dyrektywy #include, uzyskasz w pełni sprawny kod, który się kompiluje (pod warunkiem, że nie ma w nim innych błędów).

Utworzenie pliku źródłowego

By stworzyć plik nagłówkowy w Code::Blocks wystarczy utworzyć nowy pusty plik(New ? Empty File), możemy też skorzystać ze skrótu klawiszowego SHIFT+CTRL+N. Ukaże nam się komunikat, czy dodać ten plik do aktywnego projektu, oczywiście potwierdzamy.
Następnie nadajemy mu odpowiednią nazwę dla pliku nagłówkowego nazwaPliku.hpp i analogicznie dla pliku cpp nazwaPliku.cpp. Ukaże nam się jeszcze informacja o tym czy pliki mają być obsługiwane podczas Debuger czy Release

W tym wypadku można zaznaczyć obie opcje i potwierdzić OK. W ten sposób do projektu dodaliśmy dwa puste pliki. Jeśli wszystko wykonałeś to powinieneś otrzymać taki układ dla plików w projekcie.

Przykład - Pliki źródłowe

C/C++
//Plik: main.cpp
#include <iostream>
#include <conio.h>
#include "nazwaPliku.hpp"
using namespace std;
int main()
{
    cout << "Wynik dodawania to: " << dodajLiczby( 10, 15 ) << endl;
    getch();
    return( 0 );
}
C/C++
//Plik: nazwaPliku.hpp
#ifndef nazwaPliku_hpp
#define nazwaPliku_hpp

int dodajLiczby( int a, int b );

#endif
C/C++
//Plik: nazwaPliku.cpp
#include "nazwaPliku.hpp"

int dodajLiczby( int a, int b )
{
    return( a + b );
}

Przykład 2 - Lekcja 18 Funkcje z strukturami

C/C++
//Plik: main.cpp
#include "nazwaPliku.hpp"
//funkcja główna -------------
int main()
{
    using std::cout;
    using std::cin;
    //tworzenie obiektów struktur
    BokiTrojkata wprowadzDane;
    WynikiTrojkata wyswietl;
   
    cout << "Wprowadz dana bokow a, b i c: ";
    //sprytny sposób wprowadzania danych
    while( cin >> wprowadzDane.bokA >> wprowadzDane.bokB >> wprowadzDane.bokC )
    {
        wyswietl = TwierdzeniePitagorasa( wprowadzDane );
        WyswietlWyniki( wyswietl );
        cout << "\nPodaj ponownie boki lub 'k' by wyjsc.\n";
    }
    return 0;
}
C/C++
//Plik: nazwaPliku.hpp
#ifndef nazwaPliku_hpp
#define nazwaPliku_hpp
#include <iostream>
//definicja struktur ---------
struct BokiTrojkata
{
    double bokA;
    double bokB;
    double bokC;
    bool czy_prawda;
};
struct WynikiTrojkata
{
    double Wynik1;
    double Wynik2;
    double Wynik3;
    bool czy_prawda;
};
//definicje funkcji-----------
WynikiTrojkata TwierdzeniePitagorasa( BokiTrojkata pobiezboki );
void WyswietlWyniki( const WynikiTrojkata wynik );
#endif
C/C++
//Plik: nazwaPliku.cpp
#include "nazwaPliku.hpp"
#include <cmath> //nowa biblioteka
//funkcja obliczająca kwadrat poszczególnych boków A, B, C
WynikiTrojkata TwierdzeniePitagorasa( BokiTrojkata pobierzboki )
{
    // pow służy do potęgowania liczb
    using std::pow;
    //tworzy strukturę by móc ją zwrócić przez return
    WynikiTrojkata odpowiedz;
   
    if(( pow( pobierzboki.bokA, 2 ) + pow( pobierzboki.bokB, 2 ) ) == pow( pobierzboki.bokC, 2 ) )
    {
        odpowiedz.Wynik1 =( pow( pobierzboki.bokA, 2 ) );
        odpowiedz.Wynik2 =( pow( pobierzboki.bokB, 2 ) );
        odpowiedz.Wynik3 =( pow( pobierzboki.bokC, 2 ) );
        odpowiedz.czy_prawda = true;
    } else {
        odpowiedz.czy_prawda = false;
    }
    return odpowiedz; //zwraca strukturę
}
void WyswietlWyniki( const WynikiTrojkata wynik )
{
    using std::cout;
   
    if( wynik.czy_prawda )
    {
        cout << "\nTo sa boki trojkata, a dodatkowo trojkata prostokatnego!\n"
        << "Udalo sie to ustalic dzieki twierdzeniu pitagorasa.\n"
        << "Bok a * a = " << wynik.Wynik1
        << "\tBok b * b = " << wynik.Wynik2
        << "\tBok c * c = " << wynik.Wynik3
        << "\nTwierdzenie pitagorasa to: (a*a) + (b*b) = (c*c)\n"
        << "\n";
    } else
         cout << "\nPodane boki nie tworza trojkata prostokatnego(lub zadnego innego)\n\n";
   
}

Przykład 3 - Lekcja 17 Budowa aplikacji opartej na funkcjach

C/C++
//Plik: main.cpp
#include "nazwaPliku.hpp"
//funkcja główna -------------
int main()
{
    using std::string;
    const int LINIE = 6;
    string LotyKlient[ LINIE ][ BILETY ];
    int max_wierszy = 0;
    // tabela która będzie przechowywać informacje dla których miast zostały zakupione bilety
    int tab_wiersze[ 6 ] = { - 1, - 1, - 1, - 1, - 1, - 1 };
    Menu();
    //pętla zakończy się gdy Funkcja OperacjeKasjera zwróci wartość 27 (jest wartością klawisza ESC)
    while(( max_wierszy = OperacjeKasjera() ) != 27 ) {
        //sprawdza jaka opcja z menu została wybrana
        if( max_wierszy != 27 && max_wierszy != - 1 && max_wierszy != 6 ) {
            //jeżeli od 0 - 5 to wprowadzamy dana dla wybranego lotu(miasta)
            tab_wiersze[ max_wierszy ] = BazaLotow( LotyKlient, max_wierszy );
        } else if( max_wierszy == 6 ) {
            //jeżeli 6 to wyświetlamy dana dla wszystkich lotów
            Wyswietl_Dane( LotyKlient, LINIE, tab_wiersze, LINIE );
        }
        //powrót do menu
        Menu();
    }
    return 0;
}
C/C++
//Plik: nazwaPliku.hpp
#ifndef nazwaPliku_hpp
#define nazwaPliku_hpp
#include <iostream>
#include <conio.h>
#include <string>
//stała
const int BILETY = 10;
//definicje funkcji-----------
void Kursor( int, int );
void Menu();
void Wyswietl_Dane( const std::string[][ BILETY ], int, const int[], int );
int BazaLotow( std::string tabela1[][ BILETY ], int indeks1 );
int OperacjeKasjera();
#endif
C/C++
//Plik: nazwaPliku.cpp
#include "nazwaPliku.hpp"
#include "ddtconsole.h"
//FUNKCJE APLIKACJI --------------------------------------------------------------
//funkcja wyświetlająca wprowadzone dane
void Wyswietl_Dane( std::string const tabela1[][ BILETY ], int indeks1, const int tabela2[], int indeks2 )
{
    using namespace ddt::console;
    using std::cout;
    std::string Miasta[ 6 ] = { "Barcelona", "Londyn", "Paryz",
        "New York", "Moskwa", "Tokyo" };
    clrscr();
    gotoxy( 5, 5 );
    textcolor( 10 );
    cout << "Oto Dane dla linii lotniczych\n";
    textcolor( 11 );
    for( int i = 0; i < indeks2; i++ ) {
        //sprawdza czy w tabeli są dane jeżeli większe od -1 to dane są wprowadzone
        if( tabela2[ i ] != - 1 ) {
            cout << "-----------------------------\n";
            textcolor( 10 );
            cout << Miasta[ i ] << "\n";
            textcolor( 11 );
            for( int j = 0; j <= tabela2[ i ]; j++ ) {
                cout << tabela1[ i ][ j ] << "\n";
            }
            cout << "-----------------------------\n";
        }
    }
    // pod Linuxem system(
    //wyświetla komunikat systemowy i robi pauzę
    system( "pause" );
}
//funkcja odpowiadająca za kursor (strzałkę) dokładnie jej pozycja na ekranie
void Kursor( int pion, int poziom )
{
    using namespace ddt::console;
    using std::cout;
    using std::endl;
    gotoxy( pion, poziom );
    textcolor( 7 );
    cout << "->" << endl;
}
// funkcja wprowadzająca dane do tabeli
int BazaLotow( std::string tabela1[][ BILETY ], int indeks1 )
{
    using namespace ddt::console;
    using std::cin;
    using std::cout;
    std::string bufor;
    int bilety;
    int straznik = BILETY;
    int licz = 0;
    //glowna pętla wprowadzania
    for( licz; licz < straznik; licz++ ) {
        clrscr();
        gotoxy( 20, 8 );
        textcolor( 15 );
        cout << "Literki 'q' i 'Q' - koncza wprowadzenie!\n\n";
        cout << "Imie i nazwisko klienta: ";
        //pobiera dana dla danego miasta--------------------
        getline( cin, tabela1[ indeks1 ][ licz ] );
        if( tabela1[ indeks1 ][ licz ] == "q" || tabela1[ indeks1 ][ licz ] == "Q" ) {
            tabela1[ indeks1 ][ licz ] = "\0";
            return licz - 1;
        }
        gotoxy( 20, 12 );
        cout << "Liczba ujemna przerywa wprowadzanie\n";
        cout << "Ile biletow: ";
        ( cin >> bilety ).get();
        // sprawdza czy została podana liczba
        if( !cin ) //kontrola danych
        { // czyści strumień
            cin.clear();
            gotoxy( 20, 19 );
            cout << "Nie podano liczby!!! Restart!";
            //robi pauzę na czas podanyc w milisekundach
            Sleep( 4000 );
            return licz - 1;
        }
        // jeżeli zostałą podana liczba ujemna przerywa
        else if( bilety <= 0 ) {
            if( bilety == 0 ) {
                // ustawiono że dla zera bilet automatycznie jest równy 1
                bilety = 1;
            } else {
                // zeruje tabele bo wprowadzono imienie i nazwisko
                tabela1[ indeks1 ][ licz ] = "\0";
                return licz - 1;
            }
            //sprawdza ile jest dostępnych biletów
        } if( straznik >= bilety ) {
            straznik -= bilety;
            bufor = ", ilosc biletow: ";
            tabela1[ indeks1 ][ licz ] += bufor;
            bufor = 'a'; //wprowadzam znak by usunąć z bufora poprzedni łancuch
            // zamienia liczbe int na łancuch string
            std::sprintf(( char * ) bufor.c_str(), "%d", bilety );
            //łopatologiczne rozwiązanie problemu wpisania do tabeli sring liczby 10
            if( bilety == 10 ) {
                tabela1[ indeks1 ][ licz ] += bufor;
                tabela1[ indeks1 ][ licz ] += '0';
            } else
                 tabela1[ indeks1 ][ licz ] += bufor;
           
        } else {
            cout << "\nPrzekroczono liczbe biletow!!!\n"
            << "Zostalo ich tylko - " << straznik;
            //robi pauzę na czas podanyc w milisekundach
            Sleep( 5000 );
            licz -= 1;
        }
    }
    return licz - 1;
}
//funkcja do nawigacji  i obsługi menu ----
int OperacjeKasjera() {
    using namespace ddt::console;
    using std::cout;
    using std::cin;
    using std::endl;
    int znak;
    int pion = 17;
    int poziom = 12;
    // nawigacja kursorem-----------
    do
    {
        Kursor( pion, poziom );
        znak = getch();
        /*    cout << znak
                  << " -- "
                  << (int)znak;*/
        // użycie strzałek to tak naprawde dwa znaki dlatego należy usunąć pierwsz znak
        if( znak == 224 || znak == 0 )
             znak = getch();
       
        switch( znak )
        { // strzalka w dół
        case 80:
            {
                gotoxy( pion, poziom );
                cout << "  " << endl;
                if( poziom == 14 )
                     poziom = 11;
                else
                     poziom++;
               
            } break;
           
        case 72:
            { //strzałka w górę
                gotoxy( pion, poziom );
                cout << "  " << endl;
                if( poziom == 11 )
                     poziom = 14;
                else
                     poziom--;
               
            } break;
           
        case 77:
            { //strzałka w prawo
                gotoxy( pion, poziom );
                cout << "  " << endl;
                if( pion == 17 ) {
                    pion = 37;
                } else
                     pion -= 20;
               
            } break;
           
        case 75:
            { //strzałka w lewo
                gotoxy( pion, poziom );
                cout << "  " << endl;
                if( pion == 37 )
                     pion = 17;
                else
                     pion += 20;
               
            } break;
            //jeśli ENTER to
        case 13:
            { // w zaleźności od pozycji strzałki taka opcja będzie wybrana
                if( poziom == 11 && pion == 17 )
                     return 0;
                else if( poziom == 12 && pion == 17 )
                     return 1;
                else if( poziom == 13 && pion == 17 )
                     return 2;
                else if( poziom == 14 && pion == 17 )
                     return 3;
                else if( poziom == 11 && pion == 37 )
                     return 4;
                else if( poziom == 12 && pion == 37 )
                     return 5;
                else if( poziom == 13 && pion == 37 )
                     return 6;
                else if( poziom == 14 && pion == 37 )
                     return 27;
               
            } break;
            /*       default:
                   {
                      //clrscr();
                      cout << "eror" << endl;
                   } break;*/
        }
        //Gdy ESC to koniec
    } while( znak != 27 );
   
    return znak;
}
// menu aplikacji-------------
void Menu() {
    using namespace ddt::console;
    using std::cout;
    using std::endl;
    //czyści ekran
    clrscr();
    //ustawia pozycję
    gotoxy( 30, 8 );
    //zmienia kolor czcionki
    textcolor( 10 );
    cout << "Linie AirDDT" << endl;
    gotoxy( 20, 11 );
    textcolor( 11 );
    cout << "1 - Barcelona" << endl;
    gotoxy( 20, 12 );
    textcolor( 12 );
    cout << "2 - Londyn" << endl;
    gotoxy( 20, 13 );
    textcolor( 13 );
    cout << "3 - Paryz" << endl;
    gotoxy( 20, 14 );
    textcolor( 14 );
    cout << "4 - New York" << endl;
    gotoxy( 40, 11 );
    textcolor( 15 );
    cout << "5 - Moskwa" << endl;
    gotoxy( 40, 12 );
    textcolor( 13 );
    cout << "6 - Tokyo" << endl;
    gotoxy( 40, 13 );
    textcolor( 11 );
    cout << "7 - Wszystkie linie" << endl;
    gotoxy( 40, 14 );
    textcolor( 10 );
    cout << "8 - Przerwa" << endl;
    gotoxy( 30, 20 );
    textcolor( 11 );
    cout << "Esc - Zakoncz program" << endl;
    gotoxy( 30, 21 );
    textcolor( 11 );
    cout << "Enter - Zatwierdzanie" << endl;
}

Pliki nagłówkowe a źródłowe

Tworzenie projektów kilku plikowych jak widać nie jest trudne. W plikach nagłówkowych nazwaPliku.hpp powinnyśmy zawierać następujące elementy języka C++:
  • stałe symboliczne const(lub #define)
  • deklaracje funkcji (również inline), struktur, klas i szablonów
  • gdy main.cpp i nazwaPliku.cpp, korzystają z tych samych bibliotek też warto je umieścić w jednym pliku, a nie osobno dodawać jak w pokazanych wyżej przykładach
  • pliku ten nie powinien posiadać definicji funkcji i deklaracji zmiennych

Więc jak pewnie się domyślasz w pliku  nazwaPliku.cpp znajdują się funkcje zadeklarowane w pliku nagłówkowym. W ten sposób tworzy się spójność pomiędzy tymi plikami a plikiem głównym main.cpp.
Dzięki takiemu postępowaniu możemy użyć funkcji i mechanizmów zawartych w tych plikach w innych aplikacjach, bez konieczności kopiowania kodu wystarczy podpiąć odpowiednie pliki. Więc warto poświęcić trochę czasu tej tematyce, ponieważ dobrze napisane mechanizmy w plikach nagłówkowych mogą Wam się przydać w przyszłości, bez konieczności poświęcania kolejnego czasu na ich budowę.

Ćwiczenia

Znajdź wybrane zadanie z kursów i podziel je na kilka plików. Sam zdecyduj jakie to mają być zadania. Uwzględnij przy tym by nie dzielić programów, które tego nie wymagają.
Poprzedni dokument Następny dokument
Przestrzenie nazw Klasy (obiekty)