JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM Wykład 6 1
SPECYFIKATOR static Specyfikator static: Specyfikator ten powoduje, że zmienna lokalna definiowana w obrębie danej funkcji nie jest niszczona po zakończeniu działania tej funkcji i zachowuje swą wartość. Przy ponownym wejściu do tej samej funkcji, zmienna ta ma taką wartość, jaką miała przy ostatnim wyjściu z tej funkcji, np.: 2
ZAKRES WAŻNOŚCI I CZAS ŻYCIA Zakres ważności nazwy obiektu: Część programu, w której obiekt jest dostępny (nazwa jest znana). Czas życia obiektu: Okres od zdefiniowania obiektu (przydział pamięci) do momentu, gdy obiekt przestaje istnieć (zwolnienie pamięci). Dany obiekt może istnieć, lecz nie być dostępny (gdy jesteśmy poza zakresem ważności jego nazwy). Zakres globalny: Nazwa zadeklarowana na zewnątrz wszystkich funkcji ma zasięg globalny - jest znana od momentu deklaracji do końca pliku. 3
ZAKRES WAŻNOŚCI I CZAS ŻYCIA Zakres lokalny: Zakres ograniczony dwoma klamrami (zakres bloku), np.: int main() { // jakies obliczenia { int x; //definiujemy zmienną x... //możemy z x korzystać } }... //tu juz x nie jest dostepne... return 0; Zakres bloku funkcji (main lub innej): for (int i=0; i<10; ++i) { cout<<i+1 <<" "; // } Zakres ten ma etykieta jest znana nawet w linijkach poprzedzających tą etykietę. Dla przypomnienia: etykieta to nazwa zakończona dwukropkiem; można się do niej odwołać instrukcją goto. Zakres obszaru klasy: Przy omawianiu klas 4
ZASŁANIANIE NAZW Zasłanianie nazw zmiennych: o o o W programie można zdefiniować zmienną lokalną o takiej samej nazwie, jak zmienna globalna. Tak zdefiniowana zmienna lokalna zasłania (w danym lokalnym zakresie) zmienną globalną odwołanie do zmiennej to odwołanie do zmiennej lokalnej. Do zmiennej globalnej można się odwołać stosując operator zakresu ::, np.: 5
PRZESTRZENIE NAZW Przestrzenie (obszary) nazw: o o o o Nazwany zbiór symboli (np. zmiennych, funkcji). Zostały wprowadzone w celu uniknięcia konfliktów nazw np. w dużych projektach, nad którymi pracuje więcej, niż 1 jedna osoba. Symbole są zamykane w pewnych zakresach (tworzonych za pomocą słowa kluczowego namespace). Sposób wywołania nazwy z danej przestrzeni: PrzestrzenNazw::nazwa; o o Wszystkie elementy biblioteki standardowej języka C++ należą do przestrzeni nazw std. Umieszczenie przed funkcją main()deklaracji using namespace std; powoduje, że nazwy z biblioteki standardowej mogą być pisane bez przedrostka std::. 6
PRZESTRZENIE NAZW Przykład: 7
WSKAŹNIKI Zmienna to konstrukcja programistyczna posiadająca trzy podstawowe atrybuty: 1. symboliczną nazwę (identyfikator); 2. miejsce przechowywania (adres i długość danych); 3. wartość; oraz (zwykle): 4. typ (określający rodzaj danych przechowywanych w zmiennej). Wskaźnik to obiekt (zmienna typu wskaźnikowego), która przechowuje adres innego obiektu (zmiennej). Po co? o przyspieszenie operacji tablicowych; o funkcje mogą zmieniać wartość przesyłanych do nich argumentów; o dynamiczny przydział pamięci (tablice dynamiczne); o dostęp do specjalnych komórek pamięci. 8
WSKAŹNIKI Na co mogą pokazywać wskaźniki (czyli czego adres przechowywać)? - na zmienne różnych typów; - na tablice i ich elementy; - na inne obiekty. Przykłady definicji wskaźników: int *wskaz1; //wskaźnik o nazwie wskaz1 do pokazywania na obiekty typu int float * wskaz2[5]; //5-elementowa tablica wskaźników do pokazywania na obiekty typu float Zwierze * wsk; //wskaźnik do pokazywania na obiekty klasy Zwierze o Przed użyciem wskaźnika należy go przypisać do konkretnego obiektu (przypisać mu adres tego obiektu). 9
WSKAŹNIKI Przykład: 10
WTRĄCENIE......na temat liczb całkowitych Domyślnie liczby zapisywane są w systemie dziesiętnym: int l_dzies=234; Jeśli chcemy stosować system ósemkowy (oktalny) zaczynamy zerem: int l_okt=0352; przeliczając: 0352=2*8 0 +5*8 1 +3*8 2 =2+40+192=234 Jeśli chcemy stosować system szesnastkowy (heksadecymalny) zaczynamy od 0x: int l_hex=0xea; przeliczając: 0xEA=10*16 0 +14*16 1 =10+224=234 zero 11
WSKAŹNIKI o Wskaźnik można przestawić tak, by pokazywał na inny obiekt tego samego typu. o Za pomocą wskaźnika można zmodyfikować zawartość obiektu, na który ten wskaźnik pokazuje. 12
WSKAŹNIKI Do każdego wskaźnika można podstawić adres 0 ( zwany też tradycyjnie NULL), np.: lub wsk=0; wsk=null; Ustawienie wskaźnika na ten adres powoduje, że wskaźnik nie pokazuje na nic sensownego (co czasem jest pożądane, by żadnego obiektu przypadkiem nie zmodyfikować). Można np. korzystać z instrukcji warunkowej: if(wsk==0) cout<< Wskaznik nie jest ustawiony!"<<endl; //Pamiętaj: == else { } //jeśli wskaźnik pokazuje na cos sensownego... // jakieś instrukcje 13
WSKAŹNIKI RODZAJE Wskaźniki do obiektów stałych: to wskaźniki które nie mogą modyfikować obiektów na które pokazują (choć same obiekty nie muszą być stałe...): int c=111; // zwykła zmienna const int *wskaz1; //definicja wskaźnika do obiektu stałego wskaz1=&c; //ustawienie wskaźnika na obiekt //*wskaz1=222; // Błąd! Nie wolno modyfikować zawartości! Jeśli jednak mamy obiekt (zmienną) stały, to wskaźnik do pokazywania nań musi być wskaźnikiem do obiektów stałych: const int d=33; //int *wskaz2=&d ; //Błąd! nie można na stałą pokazywać zwykłym wskaźnikiem const int *wskaz2; wskaz2=&d; //OK 14
WSKAŹNIKI RODZAJE Stałe (nieruchome) wskaźniki: to wskaźniki których nie można przesuwać (ale mogą modyfikować zawartość obiektów, na które pokazują): 15
WSKAŹNIKI RODZAJE I jeszcze... stałe (nieruchome) wskaźniki do obiektów stałych : to wskaźniki łączące powyższe cechy (których nie można przesuwać i nie mogą modyfikować zawartości obiektów, na które pokazują): 16
WSKAŹNIKI RODZAJE Podsumowując Deklaracja wskaźnika ma postać: [const] typ * [const] wskaźnik co daje 4 możliwości: Typ wskaźnikowy Nazwa Dostęp do pamięci Zmiana adresu typ * wskaźnik odczyt i zapis dozwolona const typ * wskaźnik do stałej tylko odczyt dozwolona typ * const stały wskaźnik odczyt i zapis niedozwolona const typ * const stały wskaźnik do stałej tylko odczyt niedozwolona Format wykorzystywany do wypisania wskaźnika jest zależny od typu komputera. Część systemów wyświetla je jako liczby szesnastkowe (np. 0x22fef4), inne jako liczby dziesiętne (np. 2293492). 17
WSKAŹNIKI A TABLICE int tablica[5]; //definicja 5-elementowej tablicy elementów typu int int *tab_wsk=&tablica[0]; // definicja wskaźnika do typu int i ustawienie go // na element o indeksie 0 //lub: int *tab_wsk=tablica; // j.w. lub: Nazwa tablicy jest adresem jej początku! Nazwa tablicy jest stałym wskaźnikiem (takim, którego nie można przesuwać) do jej elementu o indeksie 0. Zatem możemy się wskazać np. na czwarty element tablicy (czyli element o indeksie 3) tak: int *tab_wsk=tablica; tab_wsk+=3; // teraz wskaźnik pokazuje na tablica[3] 18
WSKAŹNIKI A TABLICE Dodanie do wskaźnika liczby całkowitej powoduje, że pokazuje on o tyle właśnie elementów tablicy dalej (niezależnie od typu elementów tablicy!) int v[5]; int *v_wsk=v; (int: 4 bajty) v_wsk+=2; Wyrażenie v_wsk[i] jest równoważne *(v_wsk+i), np.: cout<<v_wsk[3]<<endl; // wypisanie zawartości elementu tablicy o indeksie 3 cout<<*(v_wsk+3)<<endl; // wypisanie zawartości elementu tablicy o indeksie 3 19
WSKAŹNIKI A TABLICE Przykład: 20
OPERACJE NA WSKAŹNIKACH Dozwolone operacje arytmetyczne na wskaźnikach to: 1. Dodawanie i odejmowanie od nich liczb naturalnych. W efekcie wskaźnik jest przesuwany na inny element tablicy (lub poza nią). 2. Odejmowanie wskaźników pokazujących na tę samą tablicę. W efekcie uzyskuje się liczbę elementów tablicy dzielących oba wskaźniki, np.: A jeśli odejmie się 2 wskaźniki pokazujące na dwie różne tablice? Sprawdź 21
OPERACJE NA WSKAŹNIKACH Porównywanie wskaźników Można stosować operatory porównania: ==!= < > <= >= Jeśli dwa wskaźniki są równe (wsk1==wsk2) to znaczy, że pokazują na te same obiekty. Jeśli dwa wskaźniki są różne (wsk1!=wsk2) to znaczy, że pokazują na różne obiekty. Używanie pozostałych operatorów porównania ma sens jedynie wtedy, jeśli pokazują na tę samą tablicę. 22
OPERACJE NA WSKAŹNIKACH Przykład: 23
WSKAŹNIKI JAKO ARGUMENTY FUNKCJI Przekazywanie argumentów do funkcji (odbieranie argumentów w funkcji) : 1. Przez wartość; 2. Przez wskaźnik ; 3. Przez referencję. Ad. 1. Przekazywanie przez wartość (przypomnienie). o Funkcja pracuje na kopii przekazywanej zmiennej, więc nie ma możliwości jej modyfikowania; o Kopia ta jest niszczona po wyjściu z funkcji. int funkcja(int x) // Funkcja odbierająca argument przez wartość { return 60*x; } //---Fragment funkcji main() int a=4, b; cout<<"a="<<a<<", b="<<b<<endl; // a=4, b=? b=funkcja(a); cout<<"a="<<a<<", b="<<b<<endl;; // a=4, b=240 24
WSKAŹNIKI JAKO ARGUMENTY FUNKCJI Ad. 2. Przekazywanie przez wskaźnik. o o Funkcja pracuje na oryginale przekazywanej zmiennej, może ją modyfikować; np.: void f16(double *a) {... // ciało funkcji } funkcja inicjalizuje wskaźnik na podstawie przekazanego adresu o Wywołanie np.: f16(&zm); przesyłamy adres zmiennej Ad. 3. Przekazywanie przez referencję. o Funkcja pracuje na oryginale przekazywanej zmiennej, może ją modyfikować; o więcej przy referencjach 25
WSKAŹNIKI JAKO ARGUMENTY FUNKCJI Przykład: 26