PROE wykład 2 operacje na wskaźnikach dr inż. Jacek Naruniec
Zmienne automatyczne i dynamiczne Zmienne automatyczne: dotyczą kontekstu, po jego opuszczeniu są usuwane, łatwiejsze w zarządzaniu od zmiennych dynamicznych Zmienne dynamiczne Są tworzone podczas wywołania new i usuwane poprzez delete, nie są związane z lokalnym kontekstem, Używamy ich kiedy nie da się w tym miejscu użyć zmiennej automatycznej
Zmienne automatyczne i dynamiczne Zaleta zmiennych dynamicznych mogą być tworzone w dowolnym momencie działania programu i wskaźniki mogą kierować do tablic o długości określonej w trakcie działania programu:
Zmienne automatyczne i dynamiczne Przykład 1 (edytor etykiet): Zmienna automatyczna, np.: główne okno aplikacji zawiera informację o pozycji okna, składowych itp., zawsze jedno główne okno aplikacji. Zmienna dynamiczna, np.: otwarte dokumenty (zmienna ilość), zawierają informacje o obiektach wewnątrz, obiekty wewnątrz dokumentu (prostokąty, paski kodów, daty itp.)
Zmienne automatyczne i dynamiczne Przykład 1I (obiekt Firma): Zmienna automatyczna, np.: Siedziba siedziba; zawiera informacje o adresie, kodzie pocztowym, itp. wiemy, że siedziba jest zawsze jedna Zmienna dynamiczna, np.: StalyKlient *stali_klienci; tablica obiektów, może się rozszerzać, może się zmniejszać. każdemu klientowi można przypisać imię, nazwisko, zniżki nie możemy na sztywno przyjąć ich liczby, nawet maksymalnej proszę pamiętać liczba pojedyncza! To jest tablica pojedynczych klientów. siedziba *stali_klienci
new, delete, wskaźniki W C++ zamiast malloc stosujemy new, a zamiast free delete. Poza tym ze wskaźników korzystamy podobnie jak w C. Przy kasowaniu pojedynczych obiektów stosujemy delete obiekt, przy kasowaniu wielu obiektów delete []obiekt W tej linijce tworzymy w pamięci tablicę 10 zmiennych typu unsigned char. Następnie przypisujemy wskaźnikowi tablica_uc adres, który wskazuje na początek tej tablicy. Tworzymy w pamięci jedną zmienną typu unsigned char. Następnie przypisujemy wskaźnikowi tablica_uc adres, który wskazuje na początek tej zmiennej w pamięci.
new, delete, wskaźniki Tak samo przypisujemy wskaźnik na pojedynczy obiekt Tak samo przypisujemy wskaźnik na początek tablicy obiektów Przypisujemy wskaźnikowi adres na początek tablicy wskaźników Obiekt* Pod każdym stworzonym wskaźnikiem wpisujemy wskaźnik na pierwszy element 20 elementowej tablicy Obiektów.
Wskaźniki Nasza pamięć: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 - zakładamy dla uproszczenia, że obiekt samochód zajmuje jeden bajt. - jeśli będzie zajmował więcej niż jeden bajt, kolejne adresy zajmowane przez samochód będą wielokrotnościami tej liczby.
Wskaźniki 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 sp jest równe 32 (tak przydzielił nam system), stworzyły się 4 obiekty samochodów w pamięci 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 Samochód spod adresu 32 (to samo) Samochód spod adresu 33 Samochód spod adresu 34 Ten wskaźnik (będzie też typu Samochod *) będzie równy 34, bo sp=32 to jest równoważne z sp[2] czyli samochodem spod adresu 34 sp2 jest równe 35 Równoważne z sp[3]
Wskaźniki Zmienna Czym jest zmienna k? Wskaźnik do koła Dostęp do obiektu koło Moment utworzenia koła Kolo k obiektem &k k, *(&k) deklaracja (Kolo k) Kolo *k wskaźnikiem k *k; k[0]; *(k[0]) Kolo k[10] wskaźnikiem k *k; k[0]; *(k[0]) k = new Kolo() deklaracja (Kolo k[10]) Moment usunięcia koła wyjście z kontekstu delete k wyjście z kontekstu
Wskaźniki 1 2 3 4 5 6 7 8 9 10 11 12 13 adr? adr? 14 adr? 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 System przydzielił tym razem adres sdp=13, pod którym stworzył tablicę trzech wskaźników do Samochodów, na razie o nieznanej wartości. 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
Wskaźniki 1 2 3 4 5 6 7 8 9 10 11 12 13 a72 a66 14 a60 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 Przypisaliśmy trzy nowe wskaźniki na obiekty(funkcja new zawsze zwraca wskaźnik!). Pierwszy wskaźnik wskazuje na obszar, w którym po kolei utworzyliśmy 4 obiekty (72), drugi na 3 obiekty (66), trzeci na 1 (60). W tym przypadku: sdp[0] jest równe 72 sdp[1] jest równe 66 sdp[2] jest równe 60 *(sdp[0]) jest samochodem spod adresu 72 *(sdp[1]) jest samochodem spod adresu 66
Wskaźniki sdp 1 2 3 4 5 6 7 8 9 10 11 12 13 a72 a66 14 a60 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 sdp[0] sdp[1] sdp[2] *(sdp[0]) jest samochodem spod adresu 72 *(sdp[1]) jest samochodem spod adresu 66 *(sdp[2]) jest samochodem spod adresu 60 sdp[0] + 2 jest równe 74, czyli *(sdp[0] + 2) jest samochodem pod adresem 74 i jest to równoznaczne z sdp[0][2] sdp[0][0]==*(sdp[0]) i jest samochodem spod adresu 72 sdp[2][0] jest samochodem spod adresu 60 sdp[1][1] jest samochodem spod adresu 67
Wskaźniki stp 1 2 3 4 5 6 7 8 9 10 11 12 a33 13 a44 14 15 16 17 18 19 20 stp[0] 21 22 23 24 25 26 27 28 29 30 stp[1] 31 32 a76 33 34 35 36 37 38 39 40 41 42 43 adr? 44 a83 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 stp[0][0] 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 stp[1][1] 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 stp[1][1][1] to samochód pod adresem 84 stp[0][0][0] to samochód pod adresem 76
Referencja Referencję określamy poprzez dodanie przedrostka & do nazwy zmiennej. Proszę nie mylić referencji z wyłuskaniem adresu do zmiennej (także &zmienna), którego wynikiem jest wskaźnik. Tutaj będzie jedynie kopia podanej zmiennej Tutaj będzie zmienna, która została podana jako parametr (bez kopiowania) liczba pozostanie 0 liczba zmieni się na 1
Referencja 1. Nie wykonujemy kopii obiektu. 2. Możemy operować bezpośrednio na tym samym obiekcie który przekazujemy. Nie przyjmuje NULL.
Konstruktor Procedura uruchamiana przy tworzeniu obiektów, wywoła się tylko jeden raz dla jednego obiektu (nie można go wywołać później). Jego nazwa musi być taka sama jak nazwa klasy. Może być bezparametrowy (konstruktor domyślny) lub z parametrami. W zależności od parametrów z jakimi tworzony jest obiekt, wywoływany jest odpowiedni konstruktor. Konstruktor który przyjmuje obiekt tego samego typu co sam obiekt nazywa się konstruktorem kopiującym.
Konstruktor
Konstruktor
Konstruktor C++11: Umożliwia przypisywanie zmiennych w deklaracji. - kompilacja w linuxie: opcja -std=c++11 - w Visual Studio automatycznie
Konstruktor Konstruktor kopiujący: Wywoływany w sytuacjach gdy potrzebujemy kopii obiektu, np.:
Konstruktor Jeśli nie ma zdefiniowanego konstruktora kopiującego, to automatycznie tworzy się domyślny kopiujący bezpośrednio wartości wszystkich pól klasy. Działa on w następujący sposób: Dlaczego w tym przypadku działanie domyślnego konstruktora będzie błędne?
Konstruktor Poprawny konstruktor kopiujący: Konstruktor domyślny zwykle nie wystarcza jeśli deklarujemy w danym obiekcie zmienne dynamiczne poprzez new/delete.
Konstruktor Konstruktory dopasowują się do parametrów uruchomi się taki konstruktor, jaki wynika z podanych parametrów. Konstruktory mogą mieć określone parametry domyślne (określone w pliku nagłówkowym!):
Konstruktor Inny sposób (powszechny) inicjalizacji składowych klasy w konstruktorze: równoważne W pierwszej kolejności należy napisać które pole ustawiamy i w nawiasie wartość parametru można korzystać z listy parametrów.
Konstruktor równoważne
Destruktor Uruchamia się w momencie usuwania obiektu danej klasy albo przy wyjściu poza kontekst albo w przypadku bezpośredniego wywołania delete. Zwykle służy do zwalniania dynamicznie zaalokowanej pamięci powiązanej z obiektem. Jest zawsze zadeklarowany w ten sam sposób:
Destruktor Musi być definiowany zawsze jeśli w obiekcie jest zaalokowana pamięć przez new, inaczej mamy wyciek pamięci!!!
Kolejność Jaka jest kolejność wykonywania konstruktorów i destruktorów?
Kolejność
Projekt Wężyk http://ztv.ire.pw.edu.pl/proe/wezyk.zip - Schemat Model -> Widok -> Kontroler (MVC Model View Controller) Kontroler komunikacja z użytkownikiem class Kontroler Widok - wyświetlanie class Widok Model logika aplikacji class Gra class Plansza class Punkt class Waz
Projekt Wężyk Main:
Projekt Wężyk Kontroler: - Zapewnia kontrolę nad aplikacją i interfejsem użytkownika (klawiaturą) - część publiczna określa zmienne/funkcje, które pozwalamy wykorzystywać innym modułom, - część prywatna zawiera zmienne/funkcje, które pozwalamy wykorzystywać tylko klasie Kontroler (i ew. klasom zaprzyjaźnionym)
Projekt Wężyk Kontroler: Widok (wyświetlanie) Logika systemu (model)
Projekt Wężyk Widok: jedynie wyświetlanie na ekran tego, co zwróci obiekt gra.
Projekt Wężyk Widok: jedynie wyświetlanie na ekran tego, co zwróci obiekt gra.
Projekt Wężyk Jedynie funkcje logiki, to jest model! Nie ma żadnego wczytywania z klawiatury/wyświetlania Tu wywoła się domyślny konstruktor obiektu plansza_gry. A co zrobić żeby wywołać inny? Skorzystać z listy inicjalizacyjnej.
Projekt Wężyk Model. typ wyliczeniowy Tu będzie się tworzyć tablica elementy_weza Wszystko poniżej dostępne jedynie dla funkcji tej klasy, funkcje spoza klasy (np. w main) nie mają do nich dostępu! tablica punktów węża na planszy
Projekt Wężyk Z zewnątrz do pól możemy dotrzeć tylko poprzez tę funkcję. Dostęp do pojedynczego elementu pola to np. pola[3][4]
Projekt Wężyk
Wężyk Konstruktor domyślny klasy Waz: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 x,y 24 x,y 25 x,y 26 x,y 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
Wężyk Konstruktor domyślny klasy Waz: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 1,1 24 1,2 25 1,3 26 1,4 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
Wężyk Konstruktor planszy
Wezyk Załóżmy wysokosc=3, szerokosc=3 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 a42 24 a64 25 a91 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42? 43? 44? 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 pola[0][0], pola[0][1], pola[0][2] pola[1][0], pola[1][1], pola[1][2] pola[2][0], pola[2][1], pola[2][2] 61 62 63 64? 65? 66? 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91? 92? 93? 94 95 96 97 98 99 100
Wezyk #define PUSTE #define SCIANA # #define WAZ s lub enum 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 a42 24 a64 25 a91 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 # 43 # 44 # 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 # 65 66 # 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 # 92 # 93 # 94 95 96 97 98 99 100
Wezyk 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 a42 24 a64 25 a91 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 # 43 # 44 # 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 # 65 66 # 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 # 92 # 93 # 94 95 96 97 98 99 100
Wezyk 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23? 24 a64 25 a91 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 y= 0 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 # 65 66 # 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 # 92 # 93 # 94 95 96 97 98 99 100
Wezyk 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23? 24? 25 a91 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 y= 1 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 # 92 # 93 # 94 95 96 97 98 99 100
Wezyk 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23? 24? 25? 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 y= 2 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
Wezyk 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100
Wezyk Akcja co 500ms:
Wężyk Co można jeszcze dodać? pojawiające się losowo jedzonko możliwość zjedzenia jedzonka licznik punktów wydłużanie się węża wybór poziomu trudności Nie wolno popsuć struktury model->widok->kontroler!