Program 6 Program wykorzystujący strukturę osoba o polach: imię, nazwisko, wiek. W programie wykorzystane są dwie funkcje: Funkcja pobierz_osobe wczytuje dane osoby podanej jako argument. Funkcja wypisz_osobe wypisująca dane osoby podanej jako argument. struct osoba string imie, nazwisko; int wiek; ; void pobierz_osobe(osoba &o); void wypisz_osobe(osoba o); const int liczba_osob = 3; osoba klasa[liczba_osob]; for (int i=0; i<liczba_osob; i++) pobierz_osobe(klasa[i]); cout << "\n\nsklad klasy: \n"; for (int i=0; i<liczba_osob; i++) wypisz_osobe(klasa[i]); void pobierz_osobe(osoba &o) cout << "imie: "; cin >> o.imie; cout << "nazwisko: "; cin >> o.nazwisko; cout << "wiek: "; cin >> o.wiek; void wypisz_osobe(osoba o)
cout << "imie: " << o.imie << "\n"; cout << "nazwisko: " << o.nazwisko << "\n"; cout << "wiek: " << o.wiek << "\n\n"; Omówienie programu Definicja struktury o nazwie osoba: struct osoba string imie, nazwisko; int wiek; ; Kilka słów o strukturach Co to jest struktura? Struktura to typ danych, podobnie jak np. typ całkowity oznaczany int. JeŜeli chcemy zdefiniować zmienną typu int o nazwie a robimy to następująco: int a; Zmienna a to jedna liczba całkowita. JeŜeli np.: a=14; wówczas zmiennej a nadajemy wartość 14. Co moŝemy opisać przy pomocy jednej zmiennej całkowitej? Np. wiek, rok, numer domu, odległość czyli wszystko to co czego wartością moŝe być jedna liczba całkowita. A czy moŝemy jedną liczbą całkowitą (czy rzeczywistą) opisać punkt na płaszczyźnie? Jedna liczba to oczywiście za mało. Musimy mieć dwie liczby poniewaŝ punkt na płaszczyźnie ma dwie współrzędne. A iloma liczbami moŝemy opisać samochód? Takich liczb jest wiele zaleŝnie od tego jak dokładnie chcemy opisać samochód np.: ilość miejsc, ilość drzwi, zuŝycie benzyny, prędkość maksymalna, pojemność bagaŝnika. W opisie samochodu moŝemy wykorzystywać nie tylko liczby, ale i ciągi znaków (typ string). Np.: marka samochodu to nie liczba to właśnie ciąg znaków. A jak opisać osobę? Osoba posiada m.in. imię i nazwisko oraz wiek. A zatem aby opisać osobę moŝemy wykorzystać liczbę (wiek) i dwa ciągi znaków (imię i nazwisko). Oczywiście moŝemy podać inne cechy osoby: numer telefonu, numer pesel, data urodzenia etc. W
powyŝszym programie wykorzystano tylko trzy: imię (typ string), nazwisko (typ string) i wiek (typ int). W tworzeniu takich złoŝonych obiektów jak omówione powyŝej (punkt na płaszczyźnie, samochód, osoba) wykorzystujemy tzw. struktury. KaŜdy z tych obiektów moŝna zadeklarować jako strukturę. Deklaracja struktury ma następującą postać: struct nazwa_struktury typ_pola1 nazwa_pola1; typ_pola2 nazwa_pola2; typ_pola3 nazwa_pola3;.......... ; RozwaŜmy na przykład strukturę punkt: struct punkt int x; int y; ; [ RównowaŜnie struct punkt int x,y; ;] Struktura punkt ma dwa pola typu int (x i y). Interpretujemy je jako współrzędne punktu. Mając zadeklarowaną strukturę punkt moŝemy definiować zmienne (nazywane teŝ obiektami) typu punkt podobnie jak moŝemy definiować zmienne typu int: punkt p; int z; W pierwszej linijce zdefiniowaliśmy zmienną o nazwie p typu punkt. W drugiej zmienną o nazwie z typu int. JeŜeli chcemy nadać wartość zmiennej p robimy to następująco: P=8;
lub cin>>p; Zmienna typu punkt posiada dwa pola całkowite (x i y). JeŜeli chcemy nadać wartości x i y zmiennej p zdefiniowanej powyŝej robimy to następująco: p.x=9; p.y=13; p.x to pole x zmiennej p, p.x to pole x zmiennej p. A zatem współrzędna x punktu p ma wartość 9, współrzędna y punktu p ma wartość 13. Oczywiście moŝemy zdefiniować inną zmienną typu punkt: punkt sk; sk.x=6; cin>>sk.y; Pole x (czyli współrzędna) punktu sk ma wartość 6. Wartość pola y wprowadzamy z klawiatury. RozwaŜmy teraz strukturę osoba z powyŝszego programu: struct osoba string imie, nazwisko; int wiek; ; Struktura ta ma trzy pola: dwa pola będące ciągami znaków (typ string) imie, nazwisko i jedno pole liczbowe (typ int) wiek. RozwaŜmy następujący program: struct osoba string imie, nazwisko; int wiek; ; osoba mama;
mama.imie="maria"; mama.nazwisko="nowak"; mama.wiek=35; cout << "imie: " << mama.imie <<endl; cout << "nazwisko: " << mama.nazwisko <<endl; cout << "wiek: " << mama.wiek <<endl; W programie tym zadeklarowana jest struktura osoba: struct osoba string imie, nazwisko; int wiek; ; Dalej zdefiniowana jest zmienna typu osoba o nazwie mama: osoba mama; Pole imie zmiennej mama ma wartość Maria. Pole nazwisko zmiennej mama ma wartość Nowak. Pole wiek zmiennej mama ma wartość 35: mama.imie= Maria ; mama.nazwisko= Nowak ; mama.wiek=35; Następnie mamy instrukcje, które wypisują na ekran pola imie, nazwisko i wiek zmiennej mama (zmienna ta jest typu osoba): cout << "imie: " << mama.imie <<endl; cout << "nazwisko: " << mama.nazwisko <<endl; cout << "wiek: " << mama.wiek <<endl; Wróćmy teraz do zasadniczego (strona 1) programu. UŜyte są w nim dwie funkcje: void pobierz_osobe(osoba &o); void wypisz_osobe(osoba o); Powtórzmy kilka faktów dotyczących funkcji w języku C++.
Kilka słów o funkcjach Funkcja (procedura, podprogram) to wydzielona część programu wykonująca jakieś operacje. Podprogramy stosuje się, aby uprościć program główny i zwiększyć czytelność kodu. RozwaŜmy następujący program: cout<<" * "<<endl; cout<<" *** "<<endl; cout<<"*****"<<endl; Wynikiem działania tego programu będzie choinka: * *** ***** Aby otrzymać trochę bardziej skomplikowaną (wyŝszą) choinkę * *** ***** * *** ***** musimy następująco zmodyfikować nasz program: cout<<" * "<<endl; cout<<" *** "<<endl; cout<<"*****"<<endl;
cout<<" * "<<endl; cout<<" *** "<<endl; cout<<"*****"<<endl; MoŜemy jednak postąpić inaczej. Zdefiniujmy funkcję choinka() która będzie rysowała: * *** ***** Za kaŝdy razem gdy będziemy chcieli uzyskać powyŝszy obrazek wywołamy funkcję choinka(). Zobaczmy jak teraz wygląda nasz program: void choinka() cout<<" * "<<endl; cout<<" *** "<<endl; cout<<"*****"<<endl; Definicja funkcji choinka() choinka(); Wywołanie funkcji choinka() Aby uzyskać wyŝszą choinkę musimy wywołać funkcję choinka() więcej razy. JeŜeli wywołamy funkcję choinka() 3 razy czyli wpiszemy w powyŝszym programie: choinka(); choinka(); choinka(); otrzymamy:
* *** ***** * *** ***** * *** ***** Funkcja choinka() nie posiada Ŝadnego argumentu. RozwaŜmy funkcję: void kwadrat(int x) Funkcja kwadrat posiada jeden argument całkowity x (int x). Działanie funkcji kwadrat polega na wypisaniu liczby całkowitej przesłanej do niej jako argument. Na przykład wywołanie funkcji: kwadrat(5); kwadrat(3); spowoduje wypisanie na ekranie liczby 25 (pierwsza linijka) i 9 (druga linijka). Cały program wygląda następująco: void kwadrat(int x) kwadrat(5); kwadrat(3);
Argumentem funkcji moŝe być dowolna zmienna takiego samego typu jak argument. Np. int c=4; kwadrat(c); W efekcie na ekranie zostanie wypisana liczba 16 czyli kwadrat liczby 4 będącej wartością zmiennej c (zauwaŝmy, Ŝe typ argumentu funkcji tzn. int jest taki sam jak typ zmiennej c). Cały program wygląda następująco: void kwadrat(int x) int c=4; cout<<c<<endl; kwadrat(c); cout<<c<<endl; Na początku mamy definicję zmiennej c typu int i nadanie jej wartości 4: int c=4; Następna linijka to wypisanie wartości zmiennej c czyli 4. W następnej linijce mamy wywołanie funkcji kwadrat(c) z argumentem c. Co się dzieje w czasie takiego wywołania funkcji kwadrat? Wartość zmiennej lokalnej (lokalnej bo określonej wewnątrz funkcji kwadrat) x będzie równa wartości zmiennej c czyli 4. Na ekran zostanie wypisana wartość x*x czyli 4*4 a zatem 16. Na końcu zostanie wypisana znowu wartość zmiennej c czyli 4.
Czyli otrzymamy: RozwaŜmy teraz następującą modyfikację funkcji kwadrat(): void kwadrat(int x) x=x*x; Pierwsza linijka odpowiedzialna za wypisanie kwadratu argumentu funkcji x jest bez zmian: W drugiej linijce mamy następującą instrukcję: x=x*x; Oznacza to, Ŝe nowa wartości zmiennej x równa jest kwadratowi dotychczasowej wartości. RozwaŜmy nasz program z tak zmodyfikowaną funkcją kwadrat: void kwadrat(int x) x=x*x; Modyfikacja int c=4; cout<<c<<endl; kwadrat(c);
cout<<c<<endl; Czy zmieni się działanie naszego programu po takiej modyfikacji funkcji kwadrat? Początek jest bez zmian - definicja zmiennej c typu int i nadanie jej wartości 4: int c=4; Następna linijka to wypisanie wartości zmiennej c czyli 4. W następnej linijce mamy wywołanie funkcji kwadrat(c) z argumentem c. W efekcie na ekranie zostanie wypisana liczba 16 (kwadrat 4) bo w funkcji mamy pierwszą linijkę: PoniewaŜ druga linijka funkcji wygląda następująco: x=x*x; zatem nowa wartość zmiennej lokalnej x będzie wynosiła 16 (bo dotychczasowa wartość była równa 4). Wartość ta nie jest jednak wypisana na ekranie. Na końcu zostanie wypisana znowu wartość zmiennej c czyli 4 (bo mamy linijka: cout<<c<<endl;). W efekcie otrzymamy: Wprowadźmy teraz pewną waŝną modyfikację do funkcji kwadrat(): void kwadrat(int &x) x=x*x; Modyfikacja zamiast int x int &x
Zobaczmy jaki będzie wynik naszego program z tak zdefiniowaną funkcją kwadrat. A zatem teraz w wyniku wywołania instrukcji: cout<<c<<endl; pojawi się nie liczba 4 ale 16! Dlaczego? W tym przypadku zamiast wartości zmiennej c czyli liczby 4 do funkcji został wysłany adres (adres zmiennej x to &x patrz definicja funkcji) zmiennej c w pamięci komputera. Ten adres funkcja sobie odebrała i stworzyła tzw. referencję czyli powiedziała sobie coś takiego: Dobrze, zatem komórce pamięci o przesłanym adresie nadaję pseudonim (nazwę) x. ZauwaŜmy, Ŝe do funkcji został przesłany adres zmiennej c bo wywołanie funkcji ma postać: kwadrat(c); A zatem wewnątrz funkcji kwadrat komórka pamięci o nazwie x to ta sama komórka co komórka o nazwie c poza funkcją. Po prostu ta sama komórka pamięci inaczej nazywa się wewnątrz funkcji kwadrat (nazwa x) i poza nią (nazwa c). W efekcie operacja: x=x*x; polega na pobraniu zawartości komórki o nazwie x (czyli liczby 4 przypomnijmy jeszcze raz, Ŝe to jest ta sama komórka która poza funkcją kwadrat ma nazwę c) i umieszczeniu w tej komórce nowej wartości równej 4*4 czyli 16. W efekcie zmianie ulega wartość zmiennej c!! I dlatego na końcu wypisana jest liczba 16. Oczywiście funkcja moŝe posiadać kilka argumentów. Zapamiętajmy na koniec, Ŝe przesłanie argumentu do funkcji bez & nazywamy przesłaniem przez wartość, natomiast przesłanie argumentu z & nazywamy przesłaniem przez referencję.
I wreszcie omówienie programu Czas powrócić (wreszcie ) do omówienia programu ze strony pierwszej. Mamy tam zdefiniowaną strukturę osoba: struct osoba string imie, nazwisko; int wiek; ; Mówiliśmy juŝ o niej powyŝej. Następnie mamy deklaracje funkcji uŝytych w programie: void pobierz_osobe(osoba &o); void wypisz_osobe(osoba o); Definicja funkcji pobierz_osoba wygląda następująco: void pobierz_osobe(osoba &o) cout << "imie: "; cin >> o.imie; cout << "nazwisko: "; cin >> o.nazwisko; cout << "wiek: "; cin >> o.wiek; ZauwaŜmy, Ŝe argument funkcji typu osoba jest przesłany przez referencję. Oznacza to, Ŝe funkcja moŝe zmienić wartości pól przesłanej do niej zmiennej typu osoba. I tak jest rzeczywiście, poniewaŝ działanie funkcji pobierz_osoba polega na pobraniu z klawiatury wartości pól imie, nazwisko i wiek zmiennej typu osoba przesłanej do funkcji przez referencję. Działanie funkcji wypisz_osoba: void wypisz_osobe(osoba o) cout << "imie: " << o.imie << "\n"; cout << "nazwisko: " << o.nazwisko << "\n"; cout << "wiek: " << o.wiek << "\n\n"; polega na wypisaniu wartości pól imie, nazwisko i wiek zmiennej typu osoba przesłanej do funkcji przez wartość.
Funkcja main() w omawianym programie wygląda następująco: const int liczba_osob = 3; osoba klasa[liczba_osob]; for (int i=0; i<liczba_osob; i++) pobierz_osobe(klasa[i]); cout << "\n\nsklad klasy: \n"; for (int i=0; i<liczba_osob; i++) wypisz_osobe(klasa[i]); Pierwsza linijka to definicja stałego obiektu typu int o nazwie liczba_osob i nadanie mu wartości 3: const int liczba_osob = 3; Słowo const oznacza, Ŝe wartość liczba_osob nie moŝe być juŝ zmieniona. Następna linijka to definicja 3-elemetowej (3-elementowej bo liczba_osob=3) tablicy typu osoba o nazwie klasa: osoba klasa[liczba_osob]; (UWAGA: tak jak moŝemy definiować tablice zawierające np. liczby całkowite (int), rzeczywiste (float) czy znaki (char) moŝemy teŝ definiować tablice zawierające osoby, jeŝeli tylko typ osoba został zdefiniowany. W naszym przypadku typ osoba został zdefiniowany bo mamy definicję struktury osoba) Następnie mamy w programie pętle for dzięki której moŝemy wpisać wartości pól imie, nazwisko i wiek w przypadku kaŝdego elementu tablicy klasa. Odbywa się to w ten sposób, Ŝe kolejno do funkcji pobierz_osobe przesyłamy jako argument: klasa[0] (dla i=0), klasa[1] (dla i=1), klasa[2] (dla i=2). W efekcie pętla równowaŝna jest trzem wywołaniom funkcji pobierz_osobe: pobierz_osobe(klasa[0]); pobierz_osobe(klasa[1]); pobierz_osobe(klasa[2]);
Druga pętla wygląda bardzo podobnie z tą róŝnicą, Ŝe zamiast funkcji pobierz_osobe wywoływana jest w niej trzy razy funkcja wypisz_osobe: wypisz_osobe(klasa[0]); wypisz_osobe(klasa[1]); wypisz_osobe(klasa[2]);