Archiwa tagu: podział kodu na pliki

C++ kurs programowania obiektowego (#6) Jak stworzyć dobry projekt (podział kodu na pliki).

Wiemy już co nieco o programowaniu obiektowym. Nie popadajmy jednak w samouwielbienie J na razie jest to niewiele ale już potrafimy coś zrobić. Przyszedł czas żeby Cię zapoznać z możliwościami dobrego rozplanowania projektu. Jak w prosty sposób poukładać sobie kod żeby był przejrzysty i żeby szybko można było dotrzeć do miejsca które chcemy odnaleźć.

Przy małych projektach nie mamy większego problemu jeżeli wszystkie klasy, metody i funkcje umieścimy w jednym pliku, głównym pliku main.cpp. Jeżeli tak podchodzimy do tematu to klasy lub funkcje umieścilibyśmy pomiędzy deklaracją przestrzeni nazw (using namespace) a główną funkcja main(). Poniżej taki właśnie przykład, program główny z dodaną klasą Smartfon (jej cechami i metodami).

Listing 6

#include <iostream>

using namespace std;

class Smartfon
{
    int waga;        // mierzona w g
    int szerokosc;   // mierzona w mm
    int wysokosc;    // mierzona w mm
    int grubosc;     // mierzona w mm
    float ile_cali;  // mierzona w calach
    int bateria;     // pojemnosc baterii mierzona w mAh

    void odbierz();
    void zadzwon();
};

int main()
{
    Smartfon smartfon1;
    smartfon1.waga = 1000;
    cout<< smartfon1.waga<<endl;
    return 0;
}

Ktoś powie, na razie na nasze potrzeby to wystarcza. Podejrzewam, że jak byśmy dopisali jeszcze kilka klas dalej nasz projekt byłby dla nas czytelny i umielibyśmy się w nim poruszać. Ale co w momencie kiedy nasz projekt rozrósł by się do większych rozmiarów np. 100 klas i każda klasa miałaby pomiędzy 50-100 linii kodu. Albo pójdźmy o krok dalej i dodajmy, że nasz program piszemy z przyjaciółmi i każdy ma określone zadania do oprogramowania i każdy pisze swoją część kodu u siebie w doku a my później musimy to połączyć w jedną działającą całość. No, przyznacie że nie jest to łatwe. Dobrze by było podzielić nasz kod na jakieś logiczne części i powiązać je ze sobą. Praca na jednym pliku nie jest podejściem dobrym. I dlatego wymyślono możliwość podziału kodu na kilka plików.

 

Żeby osiągnąć nasz zamierzony cel przeniesiemy wszystkie definicje klas do innego pliku np. klasy.cpp. Czy coś na tym zyskamy? Tak, bo już mamy oddzielony „kod główny” programu od klas. Więc tworzymy sobie nasz program w pliku main.cpp, tworząc kod wewnątrz funkcji main(), a klasy mamy w zupełnie osobnym pliku i to w nim dokonujemy potrzebnych zmian w klasach. Ale w pliku klasy.cpp dalej ciężko jest się nam poruszać. Przy niewielkich projektach, powiedzmy do kilku lub kilkudziesięciu klas, będziemy w stanie nad tym zapanować, ale my mamy nie być krótkowzroczni i myśleć przyszłościowo, jak szachiści przewidywać kolejne ruchy przeciwnika na kilka ruchów do przodu. Prędzej czy później nasz projekt się rozrośnie i jeżeli dalej wszystko będziemy mieli w jednym worku możemy w końcu przestać nad tym panować. Mam do Ciebie pytanie. Co robisz jeżeli chcesz wyszukać szybko jakąś treść w książce? Sięgasz do spisu treści! I tutaj zastosujemy podobny mechanizm. Utworzymy sobie taki dodatkowy pliczek z takim spisem treści o rozszerzeniu ‘h’ (od słówka headers, czyli nagłówki) np. klasy.h. Co nam to da? Uzyskamy dzięki temu mechanizm do szybkiego wyszukiwania metod i funkcji. W tym spisie treści szybko uzyskamy informację o wszystkich klasach ich atrybutach i metodach. Bez wchodzenia w kod danej metody będziemy wiedzieli  To tam umieszczamy komentarze po co jest ta funkcja, jak ona działa, jakie parametry przyjmuje itd.   

Jak byśmy rozbili nasz kod z listingu 6 na pliki które przed chwilą opisałem.

Plik main.cpp wyglądałby tak

Listing 7 – plik main.cpp

#include <iostream>
#include "klasy.h"

using namespace std;

int main()
{
    Smartfon smartfon1;
    smartfon1.waga = 1000;
    cout<< smartfon1.waga<<endl;
    return 0;
}

Plik klasy.h wyglądałby tak

Listing 8 – plik klasy.h

#include <iostream>

using namespace std;

class Smartfon
{
public:
    int waga;        // mierzona w g
    int szerokosc;   // mierzona w mm
    int wysokosc;    // mierzona w mm
    int grubosc;     // mierzona w mm
    float ile_cali;  // mierzona w calach
    int bateria;     // pojemnosc baterii mierzona w mAh

    void odbierz();
    void zadzwon();
};

a plik klasy.cpp wyglądałby tak

Listing 9 – Plik klasy.cpp

#include <iostream>
#include "klasy.h"

using namespace std;

    void Smartfon::odbierz()
      {
         // ciało metody odbierz 100 linii kodu
      }
    void Smartfon::zadzwon()
      {
         // ciało metody zadzwon 100 linii kodu
      }
/*
itd.  // ciała reszty metod klasy Smartfon
*/

Pewnie zauważyłeś, że w kodzie znalazły się nowe elementy.

  • w pliku main.cpp znalazł się nowy zapis #include "klasy.h"
  • w pliku klasy.cpp także znalazł się ten wpis #include "klasy.h" ale dodatkowo też Smartfon::odbierz() i Smartfon::zadzwon().

Co więc oznaczają owe tajemnicze wpisy.

  • #include "klasy.h"  – za pomocą słówka include mamy możliwość dodania nowych bibliotek ale dlaczego klasy.h jest w podwójnych cudzysłowach " ". My znamy już składnię z dwoma ostrymi nawiasami <> np. wiemy co znaczy #include <iostream>. Różnica między " " a <> jest taka, że ostre nawiasy mówią kompilatorowi, żeby szukał danej biblioteki w standardowych bibliotekach Code::Blocks’a a używając cudzysłowów " " sugerujemy kompilatorowi, że dodajemy plik z naszymi klasami który znajduje się w naszym katalogu z projektem.
    Ogólnie rzecz biorą chodzi o to, żeby plik main.cpp wiedział o istnieniu klas (cech i metod) lub funkcji zdefiniowanych w pliku klasy.h . Wyjaśnienie tego zapisu w pliku klasy.cpp jest dokładnie takie samo.
  • Smartfon::odbierz() – ponieważ może zdarzyć się sytuacja kiedy w kilku klasach będziemy mieli metodę o takiej samej nazwie napisanie np. tylko void odbierz(); nic kompilatorowi nie powie. Kompilator nie będzie wiedział o którą metodę, z której klasy, nam chodzi. Rozwiązaniem tego problemu jest wpisanie przed nazwą metody nazwę klasy z której ta metoda pochodzi a pomiędzy nimi wstawiamy operator zasięgu :: który, w naszym przypadku, oznacza, że metoda odbierz() pochodzi z klasy Smartfon.

Nie znamy jeszcze wszystkich zagadnień związanych z obiektowością więc na tym etapie kursu wystarczy, że będziemy wiedzieć musisz wiedzieć, że żeby metody i atrybuty (cechy) klasy były widoczne w programie głównym muszą być one publiczne dlatego na początku klasy Smartfon (przed definicją atrybutów) wrzuciliśmy słówko public:

 


c++ kurs programowania obiektowego spis treści  c++ kurs programowania obiektowego spis treści