Archiwa tagu: kurs programowania obiektowego

C++ kurs programowania – funkcje, przekazanie argumentów przez referencję (CodeBlocks/C++Builder)

Witam was serdecznie w kolejnym artykule poświęconym przekazywaniu argumentów do funkcji. Dzisiaj opowiem o przekazywaniu argumentów do funkcji przez referencje.

Na początku jak zwykle tworzymy jakieś zmienne i funkcje do której będziemy przekazywali argumenty przez referencje.

Na początek utworzymy sobie trzy zmienne: dlugosc, szerokosc i wysokosc i przypiszemy do nich wartości.

int dlugosc = 10;

int szerokosc = 20;

int wysokosc = 30;

 

Teraz utworzymy funkcje, do której będziemy przekazywać argumenty. Analogicznie jak przy ćwiczeniach z przekazywania argumentów przez wskaźniki utworzymy funkcję o nazwie podwoj_wymiary, która będzie miała za zadanie podwojenie wartości naszych zmiennych. W ciele funkcji od razu utworzymy kod, który ma za zadanie podwojenie wartości naszych zmiennych.

void podwoj_wymiary(int &dl, int &szer, int &wys)

{

     dl = dl * 2;

     szer = szer * 2;

     wys = wys * 2;

}

 

Oczywiście od razu słowo komentarza dotyczącego utworzonej funkcji. Na początku słówko void mówi nam tyle, że nasza funkcja nie zwraca żadnej wartości. Następnie nazwa funkcji podwoj_wymiary i nawiasy w których definiujemy argumenty funkcji. Zwróć uwagę że zdefiniowaliśmy trzy argumenty. Określiliśmy ich typy ale przed każdym z nich znajduje się znak &, który mówi o tym że argumenty te będą przekazywane przez referencje.

Teraz zostało nam wrócić do funkcji głównej, wywołać funkcje i podać jej argumenty.

podwoj_wymiery(dlugosc, szerokosc, wysokosc);

Na koniec w celu obejrzenia efektów działania funkcji dodajmy sobie linie i wyświetlmy wartości wszystkich trzech zmiennych.

cout << "Dlugosc : " << dlugosc << ", szerokosc : " << szerokosc << ", wysokosc : " << wysokosc << endl;

Na samym końcu dodajemy sobie linie instrukcją

getch();

żeby zatrzymać na chwilę program i zobaczyć efekt końcowy uruchomienia. Pamiętaj że aby instrukcja getch() zadziałała na początku musimy dodać bibliotekę conio.h.

W tym momencie nasz cały kod wygląda następująco.

Listing


#include <iostream>

#include <conio.h>

using namespace std;

 

void podwoj_wymiary(int &dl, int &szer, int &wys)

{

    dl = dl * 2;

    szer = szer * 2;

    wys = wys * 2;

}

 

int main()

{

    int dlugosc = 10;

    int szerokosc = 20;

    int wysokosc = 30;

 

    podwoj_wymiary(dlugosc, szerokosc, wysokosc);

cout << "Dlugosc : " << dlugosc << ", szerokosc : " << szerokosc << ", wysokosc : " << wysokosc << endl;

 

    getch();

    return 0;

}


Uruchamiamy program i wynikiem jego działania jest poniższa linia.

Dlugosc : 20, szerokosc : 40, wysokosc : 60

Widzisz więc, że dzięki przekazaniu argumentów przez referencje otrzymaliśmy dostęp do tych zmiennych a w instrukcjami w ciele funkcji zmieniliśmy ich wartości.

Żeby lepiej zobrazować co się stało, przed wywołaniem funkcji podwoj_wymiary wyświetlmy sobie adresy wszystkich zmiennych.

cout << "Adres zm. dlugosc : " << &dlugosc << ", adres zm. szerokosc : " << &szerokosc << ", adres zm. wysokosc : " << &wysokosc << endl;

efektem wykonania tej linii będzie

Adres zm. dlugosc : 0x61fe1c, adres zm. szerokosc : 0x61fe18, adres zm. wysokosc : 0x61fe14

I analogicznie wewnątrz funkcji podwoj_wymiary wyświetlmy sobie adresy zmiennych zdefiniowanych w funkcji.

cout << "Adres dl : " << &dl << ", adres zm. szer : " << &szer << ", adres zm. wys : " << &wys << endl;

efektem wykonania powyższej linii będzie

Adres dl : 0x61fe1c, adres zm. szer : 0x61fe18, adres zm. wys : 0x61fe14

Oczywiście u Ciebie adresy które się wyświetlają będą inne, natomiast zwróć uwagę że adres zmiennej dlugosc równa się adresowi zmiennej dl, adres zmiennej szerokosc równa się adresowi zmiennej szer, a adres zmiennej wysokosc równa się adresowi zmiennej wys.  Oznacza to tyle, że dzięki referencji otrzymaliśmy dostęp do oryginalnych zmiennych dlugosc, szerokosc i wysokosc. Zauważ że zmieniając wartości zmiennych dl, szer i wys w funkcji podwoj_wymiary zmieniasz tak naprawdę wartości zmiennych dlugosc, szerokosc i wysokosc.

Mam nadzieję że po tym przykładzie bez problemu będziesz w stanie przekazywać do funkcji argumenty przez referencje. Oczywiście zachęcam do ćwiczeń, bo tylko w taki sposób szybko utrwalisz zdobytą wiedzę. Kolejne artykuły w przygotowaniu także "do zobaczenia".

Poniżej link do kodu z tego odcinka:

CodeBlocks   C++Builder 10.2


c++ kurs programowania obiektowego spis treści  

 

 

 

 

 

C++ kurs programowania – funkcje, przekazanie argumentów przez wskaźnik (CodeBlocks/C++Builder)

Witam was serdecznie w drugim artykule z serii przekazywanie argumentów funkcji. Dzisiaj zapoznamy się z metodą przekazywania argumentów przez wskaźnik.

No to do dzieła.

Na początku za inicjujemy sobie trzy zmienne o nazwach: długosc, szerokosc, wysokosc.

int dlugosc = 10;

int szerokosc = 20;

int wysokosc = 30;

Teraz utworzymy sobie trzy wskaźniki, do każdej zmiennej po jednym. Części o wskaźnikach dowiedzieliśmy się, że wskaźnik możemy utworzyć w jednym kroku, od razu przypisując wartość do wskaźnika, albo w dwóch krokach w pierwszym kroku tworząc sam wskaźnik, a w drugim kroku przypisując temu wskaźnikowi odpowiednią wartość.

Dla pierwszej zmiennej utworzymy wskaźnik pierwszą metodą.

int *wsk_dlugosc = &dlugosc;

Utworzyliśmy wskaźnik o nazwie wsk_dlugosc i przypisaliśmy do wskaźnika adres zmiennej długosc.

Dla zmiennej szerokosc,  dla odmiany, utworzymy wskaźnik w dwóch krokach.

int *wsk_szerokosc;

wsk_szerokosc = &szerokosc;

Efekt końcowy w obu przypadkach będzie identyczny.

I został  nam jeszcze trzeci wskaźnik dla zmiennej wysokosc.

int *wsk_wysokosc = &wysokosc;

W tym momencie utworzymy sobie funkcję o nazwie podwoj_wymiary, która będzie miała za zadanie dwukrotne zwiększenie każdego z wymiarów.

Nasza funkcja będzie wyglądała jak poniżej. Pamiętaj o tym  żeby umieścić funkcję poza główną funkcją main().

void podwoj_wymiary(int *dl, int *szer, int *wys)

{

    *dl = *dl * 2;

    *szer = *szer * 2;

    *wys = *wys * 2;

}

 

Przeanalizujmy pierwszą linijkę. Na początku słówko void oznacza że funkcja nie zwraca żadnych wartości.  Następnie znajduje się nazwa funkcji, czyli u nas podwoj_wymiary.  Po nazwie funkcji  w nawiasie  znajdują się argumenty, które przekazujemy do funkcji. W naszym przypadku znajdują się tutaj definicje trzech wskaźników ze wskazaniem typów zmiennych na które wskazują. W ciele funkcji wykorzystujemy owe wskaźniki do podwojenia wartości na które wskazują te wskaźniki.

Z części o wskaźnikach wiemy, że przy definicji wskaźnika używamy znaku * dla oznaczenia że definiujemy wskaźnik. Natomiast później przy pracy ze wskaźnikiem jeżeli używamy znaku * to odwołujemy się do wartości zmiennej na którą wskazuje wskaźnik. Zerknij jeszcze raz na definicje funkcji podwoj_wymiary. W związku z powyższym co innego będzie oznaczała * w nawiasach w miejscu definicji argumentów funkcji a co innego będzie oznaczała * w ciele funkcji.

W części dotyczącej argumentów funkcji definiujemy zmienne wskaźnikowe (wskaźniki), a w ciele funkcji poprzez znak * będziemy odwoływać się do wartości zmiennej na którą wskazuje wskaźnik.

Teraz pozostaje nam tylko powrót do funkcji głównej i wywołanie funkcji podwoj_wymiary.

podwoj_wymiary(wsk_dlugosc, wsk_szerokosc, wsk_wysokosc);

Przy wywołaniu funkcji korzystamy z nazw wskaźników, więc do funkcji przekażemy adresy komórek pamięci RAM które znajdują się we wskaźnikach.

Na samym końcu pozostaje nam tylko wyświetlanie wartości zmiennych długosc, szerokosc, wysokosc.

cout << "Dlugosc : " << dlugosc << endl;

cout << "Szerokosc : " << szerokosc << endl;

cout << "Wysokosc : " << wysokosc << endl;

 

Całość naszego programu będzie wyglądał jak poniżej.

Listing


#include <iostream>

#include <conio.h>

using namespace std;

void podwoj_wymiary(int *dl, int *szer, int *wys)

{

    *dl = *dl * 2;

    *szer = *szer * 2;

    *wys = *wys * 2;

}

 

int main()

{

    // inicjalizacja zmiennych

    int dlugosc = 10;

    int szerokosc = 20;

    int wysokosc = 30;

 

    // wskazniki

    int *wsk_dlugosc = &dlugosc;

    int *wsk_szerokosc = &szerokosc;

    int *wsk_wysokosc = &wysokosc;

 

    // wywolanie funkcji

    podwoj_wymiary(wsk_dlugosc, wsk_szerokosc, wsk_wysokosc);

 

    // wyswietlenie nowych wartosci

    cout << "Dlugosc : " << dlugosc << endl;

    cout << "Szerokosc : " << szerokosc << endl;

    cout << "Wysokosc : " << wysokosc << endl;

 

    getch();

    return 0;

}


Po uruchomieniu programu na ekranie powinniśmy otrzymać następujący wynik.

Dlugosc : 20

Szerokosc : 40

Wysokosc : 60

 

Przeanalizujmy co się stało? Dlaczego wartości zmiennych długosc, szerokosc i wysokosc są dwukrotnie większe niż zdefiniowane na początku mimo, że nasza funkcja podwoj_wymiary nic nie zwraca bo jest typu void?

Odpowiedź jest prosta. Do funkcji podwoj_wymiary przekazaliśmy wskaźniki wszystkich trzech zmiennych, ale wewnątrz funkcji odwoływaliśmy się do wartości tych zmiennych, czyli pracowaliśmy na oryginalnych wartościach. Dlatego mimo że funkcja podwoj_wymiary nic nie zwraca, zmieniła wartości na które wskazują nasze wskaźniki.

Zwróć jeszcze uwagę na jedną rzecz. Przy przekazywaniu do funkcji argumentów przez wartość funkcja zwracała tylko jedną wartość. Natomiast tutaj przy przekazaniu do funkcji argumentów przez wskaźnik naraz mogliśmy zmodyfikować wartości trzech zmiennych. To także duża zaleta tej metody.

Myślę że przekazywanie argumentów przez wskaźnik nie stanowi już dla ciebie żadnego problemu. Zapraszam cię serdecznie do kolejnych artykułów.

 

Poniżej link do kodu z tego odcinka:

 

CodeBlocks   C++Builder 10.2


c++ kurs programowania obiektowego spis treści  

 

 

 

 

 

C++ kurs programowania – funkcje, przekazanie argumentów przez wartość (CodeBlocks/C++Builder)

Witam was serdecznie w kolejnym artykule. Dzisiaj pokażę Ci jak przekazać argument(-y) do funkcji przez wartość.

Otwieramy nowy projekt konsolowy w naszym Code Blocks. I przechodzimy do funkcji main(). Widok pustego projektu konsolowego w Code Blocks poniżej.

Listing


#include <iostream>

using namespace std;

int main()

{

    cout << "Hello world!" << endl;

    return 0;

}


Ustawiamy się na początku funkcji main()  i inicjujemy zmienną liczba oraz przypisujemy od razu do niej wartość 99.

int liczba = 99;

Teraz w zależności od tego co sobie wymyślimy, będziemy chcieli stworzyć funkcje i przekazać wartość zmiennej liczba do funkcji. Ta nowa funkcja powinna wykonać na tej wartości jakieś działania/ operacje a następnie powinna zwrócić nam nową wartość.

Stwórzmy więc szkielet nowej funkcji.

int zwieksz_wartosc(int liczba_kopia)

{

      // ciało funkcji, czyli działania i operacje które ma wykonać funkcja

}

Mamy już szkielet funkcji teraz omówmy sobie jej elementy. Int na początku określa jakiego typu wartości zwróci nasza funkcja. Następnie mamy nazwę funkcji, w naszym przypadku jest to zwieksz_wartosc  i od razu wiadomo co będzie robiła nasza funkcja a mianowicie zwiększała wartość zmiennej która zostanie do niej przekazana. Następnie występują nawiacy otwarte w których definiujemy argumenty funkcji.  Argumenty funkcji to wszystko to, co przekazujemy do funkcji przy jej wywołaniu. W naszym przypadku jest tylko jeden argument liczba_kopia o typie int.  Argument ten musi mieć typ int  bo do funkcji będziemy przekazywać zmienną liczba, która jest typu int.

Teraz zajmijmy się chciałem funkcji, czyli określeniem co ta funkcja będzie robiła z wartością która zostanie do niej przekazana.

liczba_kopia = liczba_kopia++;

return liczba_kopia;

Pierwsza linijka oznacza że weźmiemy wartość argumentu liczba_kopia, zwiększymy tą wartość o jeden, poprzez wykonanie inkrementacji na tej wartości przy użyciu znaków ++,  a następnie wgranie tej nowej wartość z powrotem do zmiennej liczba_kopia.

W drugiej linii słówko return oznacza, że funkcja zwróci to co znajduje się po prawej stronie od tego słówka, czyli wartość zmiennej liczba_kopia.

Reasumując.  Określiliśmy, że funkcja zwieksz_wartosc przyjmuje jeden argument typu int, następnie wartość która zostanie przekazana do funkcji będzie zwiększona o 1 i w efekcie funkcja zwróci nową  zwiększoną wartość. Zwracana wartość też ma być typu int bo tak określiliśmy na początku definicji funkcji.

Wróćmy teraz do naszego programu głównego i wywołajmy sobie naszą funkcję zwieksz_wartosc.

zwieksz_wartosc(liczba);

Wywołaliśmy funkcje z argumentem ale musimy określić gdzie chcemy "wrzucić" wynik który zwróci funkcja.

Możemy wrzucić ten wynik z powrotem do zmienne liczba i wtedy nasza linia będzie wyglądała następująco.

liczba =  zwieksz_wartosc(liczba);

ale możemy też utworzyć nową zmienną do której wrzucimy wartość z funkcji

int nowa_liczba =  zwieksz_wartosc(liczba);

lub od razu przesłać wartość zwróconą przez funkcję na ekran.

cout << zwieksz_wartosc(liczba) << endl;

Ja w naszym programie wybiorę trzecią opcję. W tym momencie ostateczny kod naszego programu wygląda jak poniżej.

Listing


#include <iostream>

using namespace std;

int zwieksz_wartosc(int liczba_kopia)

{

      liczba_kopia = liczba_kopia++;

    return liczba_kopia;

}

int main()

{

    int liczba = 99;

    cout << zwieksz_wartosc(liczba) << endl;

    return 0;

}


Po uruchomieniu naszego programu otrzymamy nową (zmienioną) wartość zmiennej liczba. Początkowo ma ona wartość 99. Następnie wywołujemy funkcję zwieksz_wartosc i jako argument funkcji podajemy zmienną liczba. Funkcja zwieksz_wartosc zwiększa wartość otrzymanej wartości o 1 i zwraca nową wartość. W efekcie funkcja zwraca nam wartość 100 którą przesyłamy na ekran.

I teraz może słówko wyjaśnienia na temat przekazania wartości zmiennej liczba do funkcji zwieksz_wartosc. Zwróć uwagę że w argumencie funkcji zwieksz_wartosc jest zdefiniowana zmienna o nazwie liczba_kopia.  Nazwa ta nie jest przypadkowa, ponieważ w momencie przekazywania wartości jakiejś zmiennej do funkcji następuje utworzenie jej kopii w pamięci i wewnątrz funkcji jest wykorzystywana kopia tej wartości a nie oryginał. Z tego też powodu funkcja musi zwracać jakąś wartość dzięki poleceniu return. Pamiętaj że utworzona kopia zmiennej w pamięci po zamknięciu funkcji, zostaje usunięta. I jeszcze jedna ważna uwaga. Jeżeli przekazujemy do funkcji artumenty przez wartość, funkcja może zwrócić tylko jedną wartość, jak w naszym przypadku return liczba_kopia.

Myślę, że przekazywanie argumentów przez wartość jest już jasne. Zapraszam Cię do kolejnych artykułów gdzie wyjaśnię przekazywanie argumentów do funkcji przez wskaźnik i referencję.

Poniżej link do kodu z tego odcinka:

CodeBlocks   C++Builder 10.2


c++ kurs programowania obiektowego spis treści  

 

 

 

 

 

C++ kurs programowania – wskaźniki (CodeBlocks)

Witam wszystkich serdecznie w kolejnym artykule. Dzisiaj zajmiemy się tematyką wskaźników.

Co to w ogóle są wskaźniki?

Wskaźniki to po prostu zmienne, nazywane zmiennymi wskaźnikowymi, które przechowują adresy innych zmiennych. Ten adres to adres w pamięci RAM zmiennej, na którą wskazuje dany wskaźnik.

No to bez zbędnych wstępów, napiszmy coś praktycznego.

Utwórz nowy projekt  konsolowy.  Standardowy kod konsolowego projektu Code Blocks wygląda jak poniżej.

 

Listing


#include <iostream>

using namespace std;

int main()

{

    cout << "Hello world!" << endl;

    return 0;

}


Zainicjujmy nową zmienną o nazwie liczba i przypiszmy jej wartośc 100.

int  liczba = 100;

Teraz utworzymy zmienną wskaźnikową (wskaźnik) o nazwie wsk_liczba, która będzie przechowywała adres w pamięci RAM zmiennej liczba.

int *wsk_liczba;

Skoro zmienna liczba jest typu int to zmienna wskaźnikowa też musi być typu int. Znak * mówi kompilatorowi, że nazwa za tym znakiem jest nazwa zmiennej wskaźnikowej.

Teraz przepiszmy adres zmiennej liczba do zmiennej wskaźnikowej wsk_liczba.

wsk_liczba = &liczba;

Przy  przy pisaniu adresu zmiennej do wskaźnika używamy znaków &.  Znak ten " wyciąga"  adres pamięci RAM tego co znajduje się po prawej stronie od znaku,  czyli w naszym przypadku adres zmiennej liczba.

Jak korzystać ze wskaźnika?

Do wskaźnika możemy odwołać się na dwa sposoby. Albo odwołać się do wartości którą przechowuje wskaźnik, czyli adres innej zmiennej w pamięci RAM. Albo odwołać się do wartości zmiennej na którą wskazuje wskaźnik.

No to zacznijmy od pracy na wskaźniku, czyli adresie który przechowuje wskaźnik. Naszą linię z „Hello world” zmodyfikuj na taką jak jest poniżej.

cout << wsk_liczba << endl;

W efekcie powinniśmy otrzymać kod jak poniżej.

 

Listing


#include <iostream>

using namespace std;

int main()

{

    int liczba = 100;

    int *wsk_liczba;

    wsk_liczba = &liczba;

    cout << wsk_liczba << endl;

    return 0;

}


PAMIĘTAJ. Jeżeli używasz innego środowiska niż Code Blocks musisz dołączyć do swojego projektu bibliotekę iostream i  ustawić odpowiednią przestrzeń nazw na std.  Code Blocks standardowo dodaje obie te linie do swojego kodu przy projektach konsolowych, więc my w tym momencie nie musimy tego robić.

Teraz możemy uruchomić nasz projekt.

Efektem uruchomienia naszego programu powinien być adres komórki na którą wskazuje wskaźnik.

0x61fe14

Oczywiście adres jest w postaci szesnastkowej.

No to teraz odwołajmy się do wartości zmiennej na którą wskazuje wskaźnik. Do naszego programu dodajmy więcej linię.

cout << *wsk_liczba << endl;

Jak widzisz Różnica polega tylko na tym że przed nazwą wskaźnika stoi znak *. W takim przypadku odwołujemy się do wartości zmiennej na którą wskazuje wskaźnik.

Uruchamiamy nasz program. W wyniku uruchomienia powinniśmy otrzymać dwie linie. Pierwsza to adres zmiennej którą przechowuje wskaźnik. A druga linia to wartość zmiennej na którą wskazuje wskaźnik.

0x61fe14

100

 

Jak widzisz wskaźniki to nie taka trudna sprawa. Zachęcam cię do samodzielnych ćwiczeń ze wskaźnikami. Zapraszam Cię również na kolejne artykuły.

Poniżej link do kodu z tego odcinka:

CodeBlocks


c++ kurs programowania obiektowego spis treści  

 

 

 

 

 

C++ kurs podstawy programowania. Instrukcja warunkowa switch case.

Instrukcja SWITCH…CASE
Innym pomysłem na rozwiązanie problemu obsługi warunków jest instrukcja switch…case (czasami nazywana instrukcją wielokrotnego wyboru). Poniżej definicja i zaraz zabieram się do objaśniania kodu.

switch(zmienna)
{
case wartosc_1:
    // jeżeli zmienna = wartość_1 to wykonaj kod który znajduje się tutaj
    break;
   
case wartosc_2:
    // jeżeli zmienna = wartość_2 to wykonaj kod który znajduje się tutaj
    break;
   
    //...
case wartosc_n:
    // jeżeli zmienna = wartość_n to wykonaj kod który znajduje się tutaj
    break;
   
default:
// kod który wykona się w momencie kiedy wartość zmiennej zmienna
   //     nie będzie oprogramowana w żadnym z powyższych przypadków
    break;
}

Na początku widzisz słówko switch i parametr w nawiasie. Tym parametrem jest wartość zmiennej i w zależności od tej wartości switch zachowa się tak jak to mu oprogramujemy. Po słówku case oprogramowujemy konkretne przypadki, co komputer ma zrobić w zależności od wartości zmiennej zdefiniowanej w parametrze instrukcji switch. Słówko break przerywa działanie instrukcji switch. Wykonamy teraz małe ćwiczenie napiszemy program który zapyta użytkownika o liczbę z przedziału od 1 do 5. Pobierzemy tą liczbę do zmiennej i w zależności od wybranej liczby wyświetlimy komunikat „Wpisales liczbe n, wiec wykonuje kod z czesci case:n”, gdzie pod n będziemy podstawiali liczbę podaną przez użytkownika. No to do dzieła.

#include <iostream>

using namespace std;

int main()
{
    int liczba;
    cout << "Podaj liczbe 1-5 : ";
    cin >> liczba;
    switch(liczba)
    {
    case 1: 
        cout << "Wpisales liczbe 1, wiec wykonuje kod z czesci case:1"; 
        break;
    case 2: 
        cout << "wpisales liczbe 2, wiec wykonuje kod z czesci case:2"; 
        break;
    case 3: 
        cout << "wpisales liczbe 3, wiec wykonuje kod z czesci case:3"; 
        break;
    case 4: 
        cout << "wpisales liczbe 4, wiec wykonuje kod z czesci case:4"; 
        break;
    case 5: 
        cout << "wpisales liczbe 5, wiec wykonuje kod z czesci case:5"; 
        break;
    
    }

    return 0;
}

Przetestujmy nasz program podając liczbę 2 (zrzut poniżej).

 cpp c++ loops switch
Widzimy, że wszystko poszło zgodnie z planem i wykonała się instrukcja dla wartości zmiennej liczba = 2.
A co się stanie jeżeli użytkownik podałby liczbę spoza zakresu np. 6 ? Przetestujmy.
 cpp c++ loops switch
Widzimy, że nic tragicznego się nie stało. Program „poszedł” dalej i nie wyświetlił żadnej instrukcji bo wartość 6 dla zmiennej liczba nie była oprogramowana w switch-u, więc kompilator nie wiedział co ma zrobić. Czasami jednak potrzebujemy oprogramować taką sytuację kiedy chcemy wykonać jakiś kod lub wyświetlić jakiś komunikat dla przypadku który jest inny niż oprogramowaliśmy w switch-u, jak np. u nas kiedy użytkownik podał liczbę 6. Do tego celu służy instrukcja default. Definiujemy ją identycznie jak w przypadku instrukcji case.

default:
   cout << "liczba nie z przedzialu 1-5, wiec nie wykonam zadnego kodu";
   break;

 Stosując instrukcję default mówimy kompilatorowi, że jeżeli nie znajdzie wartości z warunku oprogramowanej w instrukcji case to ma wykonać kod z części default. Dopiszmy tą część kodu do naszego programu i przetestujmy go.

#include <iostream>

using namespace std;

int main()
{
    int liczba;
    cout << "Podaj liczbe 1-5 : ";
    cin >> liczba;
    switch(liczba)
    {
    case 1:
        cout << "Wpisales liczbe 1, wiec wykonuje kod z czesci case:1";
        break;
    case 2:
        cout << "wpisales liczbe 2, wiec wykonuje kod z czesci case:2";
        break;
    case 3:
        cout << "wpisales liczbe 3, wiec wykonuje kod z czesci case:3";
        break;
    case 4:
        cout << "wpisales liczbe 4, wiec wykonuje kod z czesci case:4";
        break;
    case 5:
        cout << "wpisales liczbe 5, wiec wykonuje kod z czesci case:5";
        break;
    default:
        cout << "liczba nie z przedzialu 1-5, wiec nie wykonam zadnego kodu";
        break;
    }
    cout << endl;



    return 0;
}

Uruchamiamy program i wprowadzamy wartość 6.
 cpp c++ loops switch
Widzimy na ekranie komunikat który oprogramowaliśmy dla  warunku default, więc wszystko działa jak należy.

 


c++ kurs programowania obiektowego spis treści 

 

 

C++ kurs podstawy programowania. Instrukcja warunkowa if else.

Instrukcja IF ELSE
Pierwszą z instrukcji warunkowych jaką poznamy jest instrukcja if … else. Definicja if-a poniżej.

if(warunek)
   {    
      // jeżeli warunek jest spełniony, czyli przyjmuje wartość TRUE (prawdy)
      // to wykona się kod pomiędzy tymi klamrami
   }
else
   {
      // w przeciwnym wypadku wykona się ten kod tutaj
      // czyli warunek przyjmuje wartość FALSE (fałsz)
   }

Wróćmy do przykładu z kontrolowaniem prędkości. Jak mogłoby to wyglądać. Postarajmy się to oprogramować. Przyjmijmy, że co chwilę odczytujemy wartość z czujnika prędkości auta. Wartość z czujnika wrzucamy do zmiennej o nazwie aktualna_predkosc. Mamy też oprogramowane dwie funkcje. Pierwsza odcina dopływ paliwa i będzie miała nazwę zamknij_doplyw_paliwa(), a druga otwierająca z powrotem  dopływ paliwa o nazwie otworz_doplyw_paliwa(). Będzie nam potrzebna jeszcze jedna zmienna, nazwiemy ją dopływ_paliwa, zaraz wyjaśnię dlaczego. Nasz kod mógłby wyglądać jak poniżej.

if(aktualna_predkosc >= 200)
   {
      zamknij_doplyw_paliwa();
      doplyw_paliwa = "Z";
   }
else
   {
      // sprawdz czy doplyw zamkniety
      if(doplyw_paliwa == "Z")
         {
            otworz_doplyw_paliwa();
            doplyw_paliwa = "O";
         }
   }

Powyższy kod można przetłumaczyć tak. Jeżeli aktualna_predkosc jest większa lub równa 200, czyli moment kiedy przekroczyliśmy prędkość graniczną 200 km/h, to uruchom funkcję zamknij_doplyw_paliwa() i ustaw zmienną dopływ_paliwa na "Z" (zamknięty). W przeciwnym przypadku, czyli kiedy prędkość jest mniejsza niż 200 km/h, wykonaj kod z części else{}. Ale tutaj mamy drugiego if-a, bo jeszcze musimy sprawdzić czy aktualnie dopływ_paliwa jest zamknięty czy otwarty. Drugiego if-a możemy przetłumaczyć tak. Jeżeli dopływ_paliwa jest zamknięty (czyli nasz poprzednia prędkość była większa niż 200km/h) uruchamiamy funkcję otworz_doplyw_paliwa() i ustawiamy dopływ_paliwa na "O" (otwarty). Jeżeli dopływ_paliwa jest otwarty ("O") to nie robimy nic no bo aktualnie dopływ paliwa jest otwarty więc nie ma potrzeby uruchamiania funkcji otworz_doplyw_paliwa(). Myślę, że już rozumiesz dokładnie działanie instrukcji if… else i po co w ogóle była nam potrzebna zmienna dopływ_paliwa.
Zwróć uwagę, że używając instrukcji if nie musisz za każdym razem korzystać także z konstrukcji if…else możesz skorzystać tylko z if-a.
Jak już zauważyłeś if-y możemy zagnieżdżać, czyli definiować jednego if-a wewnątrz drugiego.
Cały kod programu mógłby wyglądać tak.

#include <iostream>

using namespace std;

void zamknij_doplyw_paliwa()
{
    cout << "Jedziesz jak wariat, zwolnij!" << endl;
    cout << "Zamykam doplyw paliwa!" << endl << endl;
}

void otworz_doplyw_paliwa()
{
    cout << "Widze, ze zwolniles!" << endl;
    cout << "Otwieram doplyw paliwa!" << endl << endl;
}
    int aktualna_predkosc;
    int i; // licznik dla for-a
    string doplyw_paliwa;

int main()
{



    for(i=0;i<=5;i++)
    {
        cout << "Z jaka predkoscia aktualnie jedziesz? : ";
        cin >> aktualna_predkosc;

        if(aktualna_predkosc >= 200)
           {
              zamknij_doplyw_paliwa();
              doplyw_paliwa = "Z";
           }
        else
           {
              // sprawdz czy doplyw zamkniety
              if(doplyw_paliwa == "Z")
                 {
                    otworz_doplyw_paliwa();
                    doplyw_paliwa = "O";
                 }
                 else
                 {
                     cout << "Jedziesz przepisowo, jedz tak dalej!" << endl << endl;
                 }

           }

    }

    cout << "Dziekujemy za skorzystanie z naszej nawigacji";

    return 0;
}


c++ kurs programowania obiektowego spis treści 

 

 

C++ kurs podstawy programowania. Praca z plikami tekstowymi, odczyt.

Odczyt z pliku.
Umiemy już zapisać dane do pliku tekstowego więc przyszedł czas na odczyt danych tych danych. Skoro przed chwilą napisaliśmy program do zapisu danych to pewnie wystarczy trochę go tylko zmodyfikować/przerobić na odczyt danych. Odpowiedź brzmi tak i właśnie taką modyfikację zaraz zrobimy. Przypomnienie listingu dotyczącego zapisu danych do pliku.

Listing

#include <iostream>
#include <fstream>

using namespace std;

string slowkoen, slowkopl;

int main()
{
    fstream moj_plik;
    moj_plik.open("zwierzeta.txt",ios::out | ios::app);

    cout << "Slowko angielskie: ";
    cin >> slowkoen;
    cout << "Polskie tlumaczenie: ";
    cin >> slowkopl;

    moj_plik << slowkoen << endl;
    moj_plik << slowkopl << endl;
    cout << "Slowka zapisane" << endl;

    moj_plik.close();

    return 0;
}

Zaczniemy od zmodyfikowania linii która mówi w jakim trybie otwieramy nasz plik, czy do zapisu czy do odczytu? Skoro atrybut ios::out funkcji open() mówił o tym, że otwieramy plik do zapisu to wystarczy go zmodyfikować na postać ios::in. Atrybut ios::app w ogóle usuwamy bo do naszego zadania nie jest on w ogóle potrzebny. Czyli linia dotycząca dostępu do pliku będzie wyglądała tak.

moj_plik.open( "zwierzeta.txt" , ios::in );

Dobrą praktyką jest sprawdzenie, czy z plikiem jest wszystko w porządku czy nie jest np. uszkodzony? Jeżeli będziemy chcieli odczytać jakieś dane z uszkodzonego pliku, nasz program zakończy się błędem. Do tego celu służy funkcja good(). A sprawdzenie będzie wyglądało tak.

Listing

    if(moj_plik.good()==false)
    {
        cout << "Plik jest uszkodzony!" << endl; // funkcja good() zwrocila falsz
    }
    else
    {
        cout << "Plik jest poprawny!" << endl; // funkcja good() zwrocila prawde
    } 

Funkcja good() zwróci nam wartość fałsz jeżeli plik będzie uszkodzony lub wartość prawda jeżeli z plikiem będzie wszystko OK. Jakby się tak zastanowić to w przypadku poprawnego pliku nie ma potrzeby informowania o tym fakcie użytkownika. Zamiast komunikatu o poprawnym pliku w części else{} będziemy umieszczać kod, który powie komputerowi co ma zrobić w przypadku kiedy z plikiem jest wszystko w porządku.
Skoro z plik jest OK to teraz zamiast zapisywać dane do pliku musimy je odczytać. Do tego celu służy funkcja getline(). Dla tej funkcji podajemy dwa parametry. Pierwszy to do którego pliku chcemy się odwołać (zmienna plikowa), a w drugim (po przecinku) określamy gdzie chcemy zapisać odczytane dane z pliku. Żeby mieć gdzie zapisywać dane z pliku musimy utworzyć zmienną do której będziemy je zapisywali. Stworzymy więc sobie wcześniej zmienną string linia; o typu string (pomiędzy using namespace … funkcją main() ).

string linia;

a następnie w części else{} naszego if-a wrzucimy kod z funkcją getline().

getline(moj_plik, linia);

W tym momencie nasz kod wygląda tak.

Listing

#include <iostream>
#include <fstream>

using namespace std;

string slowkoen, slowkopl;
string linia;

int main()
{
    fstream moj_plik;
    moj_plik.open("zwierzeta.txt",ios::in);

    if(moj_plik.good()==false)
    {
        // funkcja good() zwrocila falsz, blad odczytu
        cout << "Blad odczytu pliku!";
    }
    else
    {
        // funkcja good() zwrocila prawde, czyli z plikiem wszysto OK
        getline(moj_plik,linia);
        cout << linia << endl;
    }

    moj_plik.close();

    return 0;
}

Jak uruchomimy nasz program to zobaczymy na ekranie słówko cat.


cpp c++ kurs podstawy programowania odczyt z pliku tekstowego

Dlaczego tylko słówko cat ? Otóż dlatego, że funkcja getline() pobrała (odczytała) pierwszą linię z naszego pliku i tyle, na razie niczego więcej nie oprogramowaliśmy. Więc, co zrobić żeby można było „przemieszczać” się pomiędzy poszczególnymi liniami?
Każdorazowe wywołanie funkcji getline() skutkuje odczytem aktualnej linii (zaczynając od początku pliku) i przejściem do kolejnej, czyli przy drugim wywołaniu tej funkcji odczytamy drugą linię z pliku itd. W zależności jakie dane odczytujesz, co chcesz z nimi później zrobić i czy wiesz ile plik ma linii, dobierasz metodę do odczytu pliku, ale cały czas korzystasz z funkcji getline(). Weźmy na warsztat nasz plik. Wiemy, że nasz plik przechowuje w kolejnych liniach (na przemian) słówko angielskie i jego polskie tłumaczenie. Aby odczytać dane z pliku moglibyśmy wrzucić poszczególne słówka do tablicy. Wtedy mamy sytuację, że pod indeksami parzystymi (pamiętaj numeracja tablic zaczyna się od 0 a nie od 1) będziemy mieli słówka angielskie a pod indeksami nieparzystymi będziemy mieli słówka polskie. Zawsze polskie tłumaczenie danego angielskiego słówka będzie o indeksie o jeden większym niż słówko angielskie. Oczywiście pomysłów na odczytanie jest pewnie więcej np. wrzucić słówka angielskie do jednej tablicy a polskie od drugiej, wtedy komórki o takim samym indeksie o obu tablicach będą ze sobą związane (słówko angielskie i polskie tłumaczenie). Można też wrzucić słówka do tablicy wielowymiarowej itd. Teraz zajmiemy się tym rozwiązaniem o którym mówiłem na początku, wrzucimy słówka ciągiem do jednej tablicy a później je wypiszemy na ekranie ale już będziemy je pobierać z tablicy a nie z pliku. W tablicach jest tylko jedno ograniczenie, od razu musimy podać długość tej tablicy. Oczywiście możemy to obejść obliczając przed deklaracją tablicy ilość linii w pliku ale nie jest to tematem tej lekcji więc od razu przy deklaracji tablicy wpiszemy odpowiednią jej długość. No to do dzieła.
Do naszego pliku zwierzęta.txt dopisałem jeszcze trzy pozycje, więc w tym momencie mamy już 5 zwierząt: kot, piec, małpa, mysz i koń.
Na początku deklarujemy tablicę, do której wrzucimy słówka z pliku. Zwierząt jest 5 więc słówek jest 10 i tylu elementowej tablicy będziemy potrzebować. Oczywiście tablica będzie przechowywała stringi więc musi być typu string.

string words_all[10];

Teraz standardowa procedura: deklaracja zmiennej plikowej i otwarcie pliku do odczytu.

Listing

// tworzymy zmienną plikową
    fstream file;

    // otwieramy plik do odczytu
    file.open("zwierzeta.txt",ios::in);

    // good() <- sprawdza czy plik OK
    if(file.good()==false)
    {
        // plik NIE jest OK
    }
    else
    {
        // jeżeli plik nie jest uszkodzony to tutaj piszemy dalej kod
    }

Jeżeli w ten sposób „sprawdzamy” czy z plikiem wszystko OK to dalej kod piszemy wewnątrz części else{}. Można by to było oprogramować trochę inaczej.

if(file.good()==false)
    {
        // plik NIE jest OK
       exit(0);  // ale wtedy na górze musisz dodać bibliotekę #include <cstdlib>
    }
    // wtedy nie potrzebujesz części else{}
    // i kod piszesz normalnie poza if-em

Jaką robotę robi tutaj funkcja exit()? Funkcja ta służy do zamknięcia programu (wyjścia z programu). Zwróć uwagę, że uruchamiamy ją (czyli wychodzimy z programu) w momencie kiedy coś jest nie tak z plikiem. Jeżeli np. plik jest uszkodzony nie ma sensu żeby komputer dalej przetwarzał kod i najlepiej wtedy zakończyć program. Wybór metody zależy od Ciebie.
Kod który odczytuje dane z pliku i „wrzuca” je do tablicy poniżej.

// i <- licznik do for'a
        int i=0;
        for( i = 0; i <= 9; i++ )
        {
            getline(file,line);
            words_all[i] = line;
        }

Pętlę for już znamy więc wiemy jak działa. Dlaczego w drugim parametrze w pętli for jest i<=9 ? Skoro numeracja indeksów w tablicy zaczyna się od 0 to od takiej wartości licznika i musimy rozpocząć. Wtedy drugim warunkiem musi być i<=9 bo chcemy wrzucić 10 elementów do tablicy a zaczęliśmy liczyć od 0 więc musimy skończyć na wartości 9.
Znamy już funkcję getline(); i wiemy, że pobiera ona „aktualną” linię do zmiennej line.
Teraz zaczyna się clou sprawy, czyli wrzucanie tych danych do tablicy. Spójrz na zapis.

words_all[i] = line;

Wartość zmiennej line (w danym cyklu pętli for) będziemy wrzucali do kolejnych komórek tablicy words_all[]. Licznik i posłuży nam do odwoływania się do kolejnych elementów (komórek) tablicy. Przy pierwszym cyklu pętli for zmienna i będzie miała wartość 0, czyli powyższe polecenie przyjmie postać words_all[0] =line; co oznacza, że wartość (linia) odczytaną z pliku wrzucimy do komórki o zerowym indeksie w tablicy words_all[]. Przy drugim cyklu, dzięki funkcji getline() w zmiennej line będziemy już mieli odczytaną drugą linię z pliku. Wtedy licznik i dla pętli for przybierze już wartość 1, czyli drugą linię z pliku zapiszemy do komórki o indeksie 1 w tablicy words_all[] itd. Sam widzisz, że to nie jest takie proste. Pętla wykona się 10 razy i wszystkie linie (słówka) z pliku znajdą się w naszej tablicy.
Teraz wystarczy tylko wyświetlić po kolei słówka na ekranie ale już nie z pliku tylko z tablicy, gdzie przed chwilką „wrzuciliśmy” dane z pliku.

Listing

// Odczyt slowek z tablicy
        cout << "Slowka z tablicy words_all:" << endl;
        for( i = 0; i <= 9; i++ )
        {
            cout << words_all[i] << endl;
        }

Sam widzisz, że odczyt z tablicy robimy dokładnie według tego samego schematu gdy zapisywaliśmy do niej dane z tą różnicą że tutaj wywołujemy funkcję cout i odwołujemy się (za każdym „obrotem” pętli) do kolejnych komórek tablicy words_all[].
Cały kod poniżej.

Listing

#include <iostream>
#include <fstream>
#include <cstdlib>
using namespace std;

string slowkoen, slowkopl;
string linia;

int main()
{
    // deklarujemy tablice na slowka z pliku
    string words_all[10]; 
   // zm. do zapisywania linii odczytanych w pliku
    string line;

    // tworzymy zmienna plikowa
    fstream file;
    
    // otwieramy plik do odczytu
    file.open("zwierzeta.txt",ios::in);

    // good() <- sprawdza czy plik OK
    if(file.good()==false)
    {
        cout << "Blad odczytu pliku!";
        exit(0);
    }
    else
    {
        // i <- licznik do for'a
        int i=0;
        for( i = 0; i <= 9; i++ )
        {
            getline(file,line);
            words_all[i] = line;
        }

        // Odczyt slowek z tablicy
        cout << "Slowka z tablicy words_all:" << endl;
        for( i = 0; i <= 9; i++ )
        {
            cout << words_all[i] << endl;
        }
    }
    file.close();
    return 0;
}

c++ kurs programowania obiektowego spis treści 

 

 

C++ kurs podstawy programowania. Praca z plikami tekstowymi, zapis.

Dzisiaj poznamy bardzo przydatną rzecz a mianowicie nauczymy się odczytywać dane (tekst) z plików. To bardzo przydatna umiejętność. Pliki tekstowe mogą nawet służyć jako takie mini bazy danych więc sam widzisz, że umiejętność pracy z plikami tekstowymi jest naprawdę ważna i przydatna. Zaczynamy!

Startujemy oczywiście z poziomu pustego projektu Hello world! (listing poniżej).

#include <iostream>

using namespace std;

int main()
{
    cout << "Hello world!" << endl;
    return 0;
}

Stworzymy plik, który kiedyś wykorzystamy do stworzenia programu do nauki języka angielskiego. Będzie on przechowywał w kolejnych liniach najpierw słówko angielskie a w następnej linii jego polskie tłumaczenie. Nazwa pliku będzie odwoływała się do kategorii słówek, które będą się znajdowały wewnątrz pliku. Na początek stworzymy plik, który będzie przechowywał słówka do nauki zwierząt.

Żeby w ogóle pracować z plikami tekstowymi musimy dodać do naszego projektu bibliotekę poleceniem include.

#include <fstream>

Tą linię dodajemy poniżej dodanej biblioteki iostream (poniżej pierwszej linii).

Teraz możemy utworzyć zmienna plikową typu fstream, która będzie nam służyła do odwoływania się do naszego pliku. Tak jak do zapisywania ciągów znaków wykorzystujemy zmienną o typie string tak dla plików tworzymy zmienną plikową o dowolnej nazwie typu fstream.

fstream moj_plik;

Następnie musimy podać komputerowi lokalizację naszego pliku i w jakim trybie chcemy do otworzyć, bo możemy otworzyć plik albo do zapisu albo do odczytu, w sumie logiczne jeżeli chcemy coś do pliku zapisać/lub dopisać albo chcemy coś z niego odczytać. Poniżej kod.

moj_plik.open("zwierzeta.txt",ios::out);

Funkcja open() służy do otwierania pliku. Pierwszy argument w nawiasach, u nas zwierzeta.txt, mówi o ścieżce dostępu do pliku, czyli o lokalizacji pliku na dysku. Tutaj jest tylko sama nazwa pliku więc code::blocks będzie szukał naszego pliku w katalogu z projektem. Co oznacza parametr ios::out. Słówko  ios znaczy input output stream czyli zaznaczamy, że chodzi nam o strumień danych, out oznacza, że ten strumień danych chcemy wyprowadzić z programu do pliku. Znak :: to operator zasięgu mówi tylko tyle, że funkcja out jest częścią składową klasy ios.

Funkcja open() przy próbie otwarcia pliku działa jeszcze w jeden sposób. Sprawdza ona czy w podanej lokalizacji (pierwszy parametr) znajduje się plik o wskazanej przez nas nazwie. Jeżeli tak to otworzy ten plik do zapisu, a jeżeli nie to najpierw utworzy taki plik i otworzy go do zapisu. Fajnie, nawet nie musimy się za bardzo martwić, co w sytuacji kiedy naszego pliku nie ma we wskazanej lokalizacji, funkcja open() załatwi to za nas.

Żeby „wrzucić” jakieś dane do pliku utworzymy sobie dwie zmienne pomocnicze które będą nam przechowywały słówko w języku angielskim i jego tłumaczenie po polsku podane przez użytkownika.

string slowkoen, slowkopl;

Zmienne tworzymy pomiędzy using namespace std; a funkcją main().

Teraz poprosimy użytkownika o wprowadzenie z klawiatury słówka angielskiego i jego polskiego tłumaczenia.

    cout << "Slowko angielskie: ";
    cin >> slowkoen;
    cout << "Polskie tlumaczenie: ";
    cin >> slowkopl;

Czyli w zmiennych slowkoen i slowkopl mamy już słówka które chcemy zapisać w pliku. No to zapisujemy. Sprawa jest bardzo prosta.

    moj_plik << slowkoen << endl;
    moj_plik << slowkopl << endl;

Na początku odwołujesz się do Twojego pliku poprzez zmienną plikową mój_plik a później „wyprowadzasz” z programu dane z tych zmiennych do pliku. Zwróć uwagę, że na końcu każdej linii wywołujemy funkcję endl() („end line”, czyli koniec linii). Gdybyśmy tego nie zrobili to wszystkie dane, angielskie i polskie słówko, zapisałoby się do pliku w jednej linii.

Dodatkowo możemy wyświetlić użytkownikowi komunikat o tym, że słówka zostały zapisane do pliku.

cout << "Slowka zapisane" << endl;

W momencie kiedy już nie będziemy korzystać z pliku należy zamknąć plik.

moj_plik.close();

Cały nasz kod będzie wyglądał jak poniżej.

 

#include <iostream>
#include <fstream>

using namespace std;

string slowkoen, slowkopl;

int main()
{
    fstream moj_plik;
    moj_plik.open("zwierzeta.txt",ios::out);

    cout << "Slowko angielskie: ";
    cin >> slowkoen;
    cout << "Polskie tlumaczenie: ";
    cin >> slowkopl;

    moj_plik << slowkoen << endl;
    moj_plik << slowkopl << endl;
    cout << "Slowka zapisane" << endl;

    moj_plik.close();

    return 0;
}

Po uruchomieniu program pyta użytkownika o podanie słówka angielskiego

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Po jego podaniu i potwierdzeniu enterem program pyta nas o polskie tłumaczenie podanego słówka.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

 

Po podaniu polskiego słówka, program wyświetla komunikat do użytkownika, że słówko jest dodane i kończy pracę. Oczywiście code::blocks wyświetla jeszcze swoje powiadomienie o czasie wykonania programu i czeka na wciśnięcie jakiegoś klawisza z klawiatury.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Widzimy, że w konsoli wszystko poszło zgodnie z planem, ale czy utworzyliśmy pliki i czy znajdują się w nim dane które wprowadziliśmy. Zobaczmy w katalogu z projektem czy istnieje plik o nazwie zwierzęta.txt.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Widzimy, że w katalogu naszego projektu znajduje się plik zwierzęta.txt czyli wszystko wygląda dobrze. Teraz zostało nam do sprawdzenia czy po otwarciu pliku dane które wysłaliśmy do pliku się zapisały.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Wszystko zadziałało prawidłowo, w pliku znajdują się dwie linie z zapisanymi słówkami. Ktoś jednak przytomnie spyta co się stanie w przypadku ponownego uruchomienia programu. Czy program dopisze dane do pliku czy je nadpisze. No to uruchamiamy program jeszcze raz.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Sprawdzamy co się dzieje w pliku.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Widzimy więc, że każde uruchomienie programu powoduje nadpisanie wcześniej wprowadzonych danych. Czy możemy coś z tym zrobić? Oczywiście, że tak. W miejscu gdzie określamy w jakim trybie otwieramy nasz plik musimy dodać parametr mówiący o tym, że chcemy dopisywać dane a nie zastępować. Wystarczy do linii

moj_plik.open("zwierzeta.txt",ios::out);

dodać parametr

moj_plik.open("zwierzeta.txt",ios::out | ios::app);

Sprawdźmy czy działa. Aktualnie w naszym pliku mamy dodanego psa. No to nie może zabraknąć także kota. Uruchamiamy program i dodajemy słówka cat / kot.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Sprawdźmy co się stało w naszym plik. Otwieramy plik zwierzęta.txt.

cpp c++ kurs podstawy programowania, progarmowanie obiektowe

Bingo! Pod słówkami dotyczącymi psa dodały się dwie linie dotyczące kota. Oto właśnie nam chodziło. Widzisz więc, że zapisywanie danych do pliku tekstowego to nic trudnego. Finalnie nasz kod przybierze postać.

#include <iostream>
#include <fstream>

using namespace std;

string slowkoen, slowkopl;

int main()
{
    fstream moj_plik;
    moj_plik.open("zwierzeta.txt",ios::out | ios::app);

    cout << "Slowko angielskie: ";
    cin >> slowkoen;
    cout << "Polskie tlumaczenie: ";
    cin >> slowkopl;

    moj_plik << slowkoen << endl;
    moj_plik << slowkopl << endl;
    cout << "Slowka zapisane" << endl;

    moj_plik.close();

    return 0;
}

W kolejnych częściach kursu nauczysz się odczytywać te dane z pliku.


c++ kurs programowania obiektowego spis treści 

 

 

C++ kurs podstawy programowania. Zmienne. Typy danych.

Tworząc programy komputerowe często musimy zapisywać (zapamiętać) różne dane, które w dalszej części będziemy wykorzystywali do różnych celów np. proste równanie matematyczne a+b. Jak stworzylibyśmy program który dodaje dwie liczby do siebie? Na początku poprosilibyśmy użytkownika o podanie pierwszej liczby, później o podanie drugiej i na końcu dodalibyśmy obie wartości do siebie. Do dobrze, ale gdzie i jak komputer zapisze te wartości które poda użytkownik? Otóż komputer zapisuje te wartości w pamięci komputera pod nazwą którą poda mu programista, bo przecież my jako programiści jakoś do tej wartości później musimy się odwołać, jakoś ją zidentyfikować. My jako programiści nie musimy wiedzieć gdzie konkretnie ta wartość się w pamięci znajduje, wystarczy, że wiemy jak się do niej odwołać. To miejsce, o konkretnej nazwie, które będzie przechowywało jakąś wartość nazywamy zmienną. Można to przyrównać do wielkiej komody (pamięć komputera) z wieloma szufladami. Taką jedną pojedynczą szufladę będziemy nazywamy zmienną. Do naszej szuflady możemy „włożyć” jakąś wartość (określonego typu, o typach za chwilę). Szufladę musimy jakoś nazwać żebyśmy wiedzieli, którą szufladę w danym momencie chcemy otworzyć. Poniżej przykład deklaracji zmiennej, która będzie przechowywała liczbę całkowitą. Deklaracja to takie przygotowanie naszej szuflady na wrzucenie konkretnej wartości, ale jeszcze tej wartości do szuflady nie wrzucamy. Przygotowując tą szufladę musimy „powiedzieć” komputerowi dokładnie jakiego typu zmienne będzie tam można przechowywać, czyli określamy typ zmiennej. Jeżeli określimy, że do zmiennej (szuflady) możemy wrzucić liczbę całkowitą (typ int) to będziemy do niej mogli wrzucić tylko wartości tego typu i żadnej innej, nie wrzucimy tam np. jakiegoś tekstu bo ta szuflada przyjmie tylko liczbę całkowitą.

int liczba;

gdzie: int oznacza typ zmiennej, czyli typ przechowywanej wartości, a liczba to nazwa zmiennej.

Zauważ, że w tym momencie mamy już zdefiniowaną zmienną, czyli określiliśmy jakie wartości (jakiego typu) ta zmienna może przyjąć (przechowywać) i określiliśmy jej nazwę ale nie przypisaliśmy do tej zmiennej żadnej wartości. Przypisanie wartości do zmiennej nazywamy inicjalizacją, patrz kod poniżej.

liczba = 1;

Zobaczy, że deklaracja i inicjalizacja zajmują dwie linijki kodu. Czy można to zrobić szybciej? Tak. Poniżej przykład deklaracji i inicjalizacji zmiennej w jednej linijce.

int liczba = 1;

Powyższy zapis oznacza, że w pamięci komputera „siedzi” zmienna o nazwie liczba, która w tym momencie przechowuje wartość 1, czyli liczbę całkowitą. To, że w zmiennej liczba możemy zapisać tylko liczbę całkowita określa nam typ zmiennej przed jej nazwą, czyli int.

Do naszej zmiennej możemy w każdym momencie przypisać nową wartość np.

int liczba = 2;

W tym momencie nasza zmienna o nazwie liczba przechowuje już wartość 2. Czyli wartość 1 została zastąpiona wartością 2. Jeżeli odwołamy się w tym momencie do tej zmiennej np. będziemy ją chcieli wyświetlić na ekranie to wyświetlimy liczbę 2.

cout << liczba << endl;

Czyli cały nasz program przybierze formę.

#include <iostream>
using namespace std;
int main()
{
    int liczba = 1;
    liczba = 2;
    cout << liczba << endl;
    return 0;
}

 

A poniżej efekt uruchomienia naszego programu

cpp c++ kurs programowania podstawy zmienne typy danych

Teraz przejdziemy do tematu typów danych. Już poznałeś typ int, który mówi nam, że możemy do zmiennej zapisać tylko liczbę całkowitą. No ale my, do tworzenia naszego programu, potrzebujemy zapisywać inne wartości np. liczby zmiennoprzecinkowe, ciągu znaków itd. Spokojnie i takie dane możemy zapisywać w zmiennych wykorzystując odpowiedni typ. Wykaz najpopularniejszych typów danych przedstawiam Ci poniżej.

cpp c++ kurs programowania podstawy zmienne typy danych

 

Dla przykładu, jeżeli będziemy chcieli zapisać a później wyświetlić jakiś ciąg znaków nasz kod będzie wyglądał tak.

#include <iostream>

using namespace std;

int main()
{
    string tekst; // deklaracja zmiennej o nazwie tekst i typie string
    tekst = "Ala ma kota"; // przypisanie zmiennej tekst wartości, czyli zdania  „Ala ma kota”.
    cout << tekst << endl; // wyświetlenie zawartości zmiennej tekst na ekranie
    return 0;
}

Efekt uruchomienia programu poniżej.

cpp c++ kurs programowania podstawy zmienne typy danych

 

Myślę, że jesteś już gotowy na samodzielne napisanie swojego pierwszego programu. Poniżej opis.

Zadanie.

Firma wypracowała w danym roku zysk, którym chce się podzielić z wszystkimi pracownikami. 80% zysku zostanie przeznaczona na premie dla wszystkich pracowników (po równo dla każdego), kolejne 15 % przeznaczone będzie wyłącznie dla kadry kierowniczej a dodatkowe 5% dla prezesa. Jaką premię otrzyma każdy z pracowników firmy (pracownik produkcji, kierownik, prezes)? Zakładamy, że prezes jest jeden a liczbę pracowników produkcji i kadry kierowniczej podaje użytkownik.

Przy założeniu, że kwota zysku to 10000 zł, liczba pracowników produkcji to 10 osób, liczba kierowników to 3 osoby i prezesie, premie rozkładają się następująco:

  • Premia dla pracownika produkcji: 571,429 zł
  • Premia dla członka kadry kierowniczej: 946,429 zł
  • Premia dla Prezesa: 2017,86 zł

Poniżej kod programu.

#include <iostream>

using namespace std;

int liczbaPracownikow, liczbaKierownikow;
float zysk, zyskPracownicy, zyskKierownicy, zyskPrezes;
float premiaPracownik, premiaKierownik, premiaPrezes;

int main()
{
    cout << "Jaki zysk wypracowala firma: ";
    cin >> zysk;

    cout << "Podaj liczbe pracownikow dzialu produkcji: ";
    cin >> liczbaPracownikow;

    cout << "Podaj liczbe czlonkow kadry kierowniczej: ";
    cin >> liczbaKierownikow;

    premiaPracownik = zysk*0.8/(liczbaPracownikow+liczbaKierownikow+1);
    premiaKierownik = zysk*0.15/ (liczbaKierownikow + 1) + premiaPracownik;
    premiaPrezes = zysk*0.05 + premiaPracownik + premiaKierownik;

    cout << "Premia dla pracownika produkcji: " << premiaPracownik << endl;
    cout << "Premia dla czlonka kadry kierowniczej: " << premiaKierownik << endl;
    cout << "Premia dla Prezesa: " << premiaPrezes << endl;

    return 0;
}

Zwróć uwagę na kilka haczyków. Pierwszy haczyk jest taki, że to 80% musimy rozdzielić pomiędzy wszystkich pracowników firmy, czyli pomiędzy pracowników produkcji ale także między kierowników i prezesa, bo oni także są pracownikami firmy. Drugi haczyk jest taki, że prezes to także członek kadry kierowniczej, czyli 15% zysku przeznaczone dla kadry kierowniczej musimy podzielić przez liczbę kierowników + prezes. I stąd te wyniki.

 


 

c++ kurs programowania obiektowego spis treści 

 

 

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