Programowanie obiektowe i C++ dla matematyków

Podobne dokumenty
2.4 Dziedziczenie. 2.4 Dziedziczenie Przykłady programowania w C - kurs podstawowy

Programowanie obiektowe i C++ dla matematyków

Klasa jest nowym typem danych zdefiniowanym przez użytkownika. Najprostsza klasa jest po prostu strukturą, np

TEMAT : KLASY DZIEDZICZENIE

Programowanie obiektowe i C++ dla matematyków

Mechanizm dziedziczenia

Programowanie obiektowe i C++ dla matematyków

Programowanie w C++ Wykład 13. Katarzyna Grzelak. 4 czerwca K.Grzelak (Wykład 13) Programowanie w C++ 1 / 26

Laboratorium 1 - Programowanie proceduralne i obiektowe

Podczas dziedziczenia obiekt klasy pochodnej może być wskazywany przez wskaźnik typu klasy bazowej.

Enkapsulacja, dziedziczenie, polimorfizm

C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie. C++ - dziedziczenie C++ - DZIEDZICZENIE.

IMIĘ i NAZWISKO: Pytania i (przykładowe) Odpowiedzi

Materiały do zajęć VII

Język C++ wykład VII. uzupełnienie notatek: dr Jerzy Białkowski. Programowanie C/C++ Język C++ wykład VII. dr Jarosław Mederski. Spis.

Programowanie w C++ Wykład 11. Katarzyna Grzelak. 13 maja K.Grzelak (Wykład 11) Programowanie w C++ 1 / 30

Java - tablice, konstruktory, dziedziczenie i hermetyzacja

Informacje ogólne. Karol Trybulec p-programowanie.pl 1. 2 // cialo klasy. class osoba { string imie; string nazwisko; int wiek; int wzrost;

Obiekt klasy jest definiowany poprzez jej składniki. Składnikami są różne zmienne oraz funkcje. Składniki opisują rzeczywisty stan obiektu.

Wstęp do programowania obiektowego. WYKŁAD 3 Dziedziczenie Pola i funkcje statyczne Funkcje zaprzyjaźnione, this

Podstawy algorytmiki i programowania - wykład 4 C-struktury

TEMAT : KLASY POLIMORFIZM

Wykład V. Programowanie II - semestr II Kierunek Informatyka. dr inż. Janusz Słupik. Wydział Matematyki Stosowanej Politechniki Śląskiej

Mechanizm dziedziczenia

Programowanie 2. Język C++. Wykład 3.

Programowanie obiektowe

dr inż. Jarosław Forenc

Programowanie w C++ Wykład 12. Katarzyna Grzelak. 28 maja K.Grzelak (Wykład 12) Programowanie w C++ 1 / 27

Dziedziczenie & W slajdach są materiały zapożyczone z

Programowanie komputerowe. Zajęcia 7

Programowanie w C++ Wykład 12. Katarzyna Grzelak. 20 maja K.Grzelak (Wykład 12) Programowanie w C++ 1 / 32

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

JĘZYKI PROGRAMOWANIA Z PROGRAMOWANIEM OBIEKTOWYM

Dariusz Brzeziński. Politechnika Poznańska, Instytut Informatyki

Kurs programowania. Wykład 2. Wojciech Macyna. 17 marca 2016

Programowanie II. Lista 3. Modyfikatory dostępu plik TKLientBanku.h

Programowanie obiektowe

Kurs programowania. Wstęp - wykład 0. Wojciech Macyna. 22 lutego 2016

Podstawy Programowania Obiektowego

Wykład 8: klasy cz. 4

Konstruktory. Streszczenie Celem wykładu jest zaprezentowanie konstruktorów w Javie, syntaktyki oraz zalet ich stosowania. Czas wykładu 45 minut.

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Dziedziczenie. Streszczenie Celem wykładu jest omówienie tematyki dziedziczenia klas. Czas wykładu 45 minut.

PARADYGMATY PROGRAMOWANIA Wykład 2

KLASA UCZEN Uczen imię, nazwisko, średnia konstruktor konstruktor Ustaw Wyswietl Lepszy Promowany

Techniki programowania INP001002Wl rok akademicki 2017/18 semestr letni. Wykład 4. Karol Tarnowski A-1 p.

Programowanie obiektowe, wykład nr 6. Klasy i obiekty

Do czego służą klasy?

Techniki programowania INP001002Wl rok akademicki 2018/19 semestr letni. Wykład 3. Karol Tarnowski A-1 p.

private - oznacza, że wszystkie elementy klasy bazowej zmieniają się w prywatne.

Programowanie obiektowe

W2 Wprowadzenie do klas C++ Klasa najważniejsze pojęcie C++. To jest mechanizm do tworzenia obiektów. Deklaracje klasy :

Aplikacje w środowisku Java

Dziedziczenie. Ogólna postać dziedziczenia klas:

Informatyka II Laboratorium 3 : Programowania obiektowe C++ - dziedziczenie

Programowanie obiektowe Wykład 7. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/20

Wykład 4: Klasy i Metody

1. Które składowe klasa posiada zawsze, niezależnie od tego czy je zdefiniujemy, czy nie?

Wstęp do Programowania 2

Zaawansowane programowanie w języku C++ Klasy w C++

Programowanie obiektowe Wykład 6. Dariusz Wardowski. dr Dariusz Wardowski, Katedra Analizy Nieliniowej, WMiI UŁ 1/14

Typy klasowe (klasy) 1. Programowanie obiektowe. 2. Założenia paradygmatu obiektowego:

10. Programowanie obiektowe w PHP5

Wprowadzenie w dziedziczenie. Klasa D dziedziczy klasę B: Klasa B klasa bazowa (base class), klasa D klasa pochodna (derived class).

Polimorfizm, metody wirtualne i klasy abstrakcyjne

Różne właściwości. Różne właściwości. Różne właściwości. C++ - klasy. C++ - klasy C++ - KLASY

Dziedziczenie. dr Jarosław Skaruz

Listy powiązane zorientowane obiektowo

Składnia C++ Programowanie Obiektowe Mateusz Cicheński

wykład IV uzupełnienie notatek: dr Jerzy Białkowski Programowanie C/C++ Język C, a C++. wykład IV dr Jarosław Mederski Spis Język C++ - wstęp

Programowanie obiektowe i C++ dla matematyków

znajdowały się różne instrukcje) to tak naprawdę definicja funkcji main.

Zaawansowane programowanie w języku C++ Programowanie obiektowe

public: // interfejs private: // implementacja // składowe klasy protected: // póki nie będziemy dziedziczyć, // to pole nas nie interesuje

Programowanie Obiektowo Zorientowane w języku c++ Przestrzenie nazw

Wprowadzenie do programowanie obiektowego w języku C++

Programowanie współbieżne Wykład 8 Podstawy programowania obiektowego. Iwona Kochaoska

Wprowadzenie do programowanie obiektowego w języku C++

Dokumentacja do API Javy.

Informatyka I. Klasy i obiekty. Podstawy programowania obiektowego. dr inż. Andrzej Czerepicki. Politechnika Warszawska Wydział Transportu 2018

Programowanie obiektowe w języku

#include <iostream> using namespace std; void ela(int); int main( ); { Funkcja 3. return 0; }

Projektowanie klas c.d. Projektowanie klas przykład

Informatyka I. Dziedziczenie. Nadpisanie metod. Klasy abstrakcyjne. Wskaźnik this. Metody i pola statyczne. dr inż. Andrzej Czerepicki

Języki i techniki programowania Ćwiczenia 3 Dziedziczenie

Operator przypisania. Jest czym innym niż konstruktor kopiujący!

Wykład 5 Okna MDI i SDI, dziedziczenie

Wykład 1. Program przedmiotu. Programowanie Obiektowe (język C++) Literatura. Program przedmiotu c.d.:

Programowanie Obiektowe i C++

referencje Wykład 2. Programowanie (język C++) Referencje (1) int Num = 50; zdefiniowano zmienną Num (typu int) nadając jej wartość początkową 50.

Programowanie obiektowe i zdarzeniowe

Technologie cyfrowe semestr letni 2018/2019

Struktury Struktura polami struct struct struct struct

Wyjątki (exceptions)

C-struktury wykład. Dorota Pylak

Programowanie obiektowe - 1.

Wykład 5: Klasy cz. 3

Dziedziczenie. Tomasz Borzyszkowski

Programowanie obiektowe język C++

Zaawansowane programowanie w C++ (PCP)

Transkrypt:

Programowanie obiektowe i C++ dla matematyków Bartosz Szreder szreder (at) mimuw... 13 XII 2011 1 Dziedziczenie Mamy dwa światy: gorszy (rzeczywisty) i lepszy (komputerowy). Lepsiejszość drugiego polega na prostocie i uporządkowaniu. Zabawa polega na psuciu drugiego świata poprzez modelowanie rzeczywistości. O przedmiotach w świecie rzeczywistym często myślimy w sposób bardzo ogólny, przykładowo taki kosz na śmieci może być: pojemnikiem wyłącznie na puszki albo szkło, wielkim kontenerem pod blokowiskiem, kuchennym pojemnikiem na ziemniaczane obierki. O każdym z tych obiektów możemy powiedzieć zarówno, że są klasy KoszNaSmieci jak i określić je bardziej szczegółowo, np. Kontener. Tym niemniej każdy kosz na śmieci, niezależnie od swojej specjalizacji, będzie miał pewne cechy wspólne z każdym innym koszem na śmieci, chociażby objętość. Będziemy zatem mówić, że KoszNaSmieci jest klasą bazową, Kontenter zaś klasą pochodną. W kontekście języka C++ oznacza to tyle, że jeśli weźmiemy sobie klasę KoszNaSmieci, która będzie miała pewną objętość, np. int objetosc (atrybut klasy), to każda klasa pochodna także będzie miała ten atrybut. W dodatku możemy określić niektóre metody klasy bazowej w taki sposób, żeby były one dostępne także w klasie pochodnej na przykład każdy śmietnik będziemy chcieli kiedyś opróżnić, więc zapewne przyda się metoda w rodzaju void oproznij(smieciarz *). Takie wydzielanie pewnych wspólnych atrybutów i zachowań w postaci metod jest istotną częścią programowania zorientowanego obiektowo. Uzyskujemy mechanizm, który w praktyce potrafi skupić sporą część logiki programu w jednym miejscu (w sensie ułatwienia nieprogramowania copypaste) oraz niezwykle silną zdolność rozwijania istniejących hierarchii klas w przyszłości. Będzie to widoczne w momencie wprowadzenia tzw. polimorfizmu. Odkładając na razie na bok metafizyczne rozważania, spróbujmy wziąć jakiś kawałek kodu i zobaczyć co on robi (przykład z ćwiczeń): 001 #include <iostream> 002 #include <string> 003 004 using namespace std; 005 006 class Zwierz { 1

007 public: 008 string imie; 009 010 string dajglos() const 011 { 012 return "nie wiem czym jestem, ale nazywam sie " + imie + "\n"; 013 } 014 015 Zwierz(const string &s) 016 { 017 imie = s; 018 } 019 }; 020 021 class Kot : public Zwierz { 022 public: 023 string dajglos() const 024 { 025 return "jestem kotem i nazywam sie " + imie + "\n"; 026 } 027 028 Kot(const string &s) 029 : Zwierz(s) {} 030 }; 031 032 class Pies : public Zwierz { 033 public: 034 string dajglos() const 035 { 036 return "jestem psem i nazywam sie " + imie + "\n"; 037 } 038 039 Pies(const string &s) 040 : Zwierz(s) {} 041 }; 042 043 int main() 044 { 045 Zwierz Z("Metan"); 046 Kot K("Etan"); 047 Pies P("Propan"); 048 049 cout << Z.dajGlos(); 050 cout << K.dajGlos(); 051 cout << P.dajGlos(); 052 return 0; 2

053 } Pojawiło się kilka nowych elementów językowych. Zapisaliśmy pewne klasy w formie B : public A. Oznacza to, że klasa B jest klasą pochodną względem klasy A, ewentualnie klasa B dziedziczy po klasie A. Każde pole składowe klasy bazowej będzie także występowało w klasie pochodnej (co widać po użyciu zmiennej imie w metodach dajglos()). Nie znaczy to jednakże, że zawsze będziemy mieli w klasie pochodnej dostęp do każdego pola składowego klasy bazowej, ale o tym poniżej. Ze względu na ograniczony czas zajęć będziemy skupiali się tylko na dziedziczeniu publicznym, tak jak w przykładach powyżej użycie po dwukropku słowa public jest znaczące. Zakładamy od teraz, że jeśli mówimy o dziedziczeniu, to chodzi o dziedziczenie publiczne i nie wnikamy jak działają inne typy dziedziczenia. 1.1 Listy inicjalizacyjne Konstruktory dla kota i psa mają niespotykany wcześniej wygląd. Użyte w nich zostały tzw. listy inicjalizacyjne, czyli wywołania konstruktorów dla klasy bazowej i dla pól składowych. Czasami użycie list inicjalizacyjnych jest tylko przyjemne, czasami także niezbędne. Należy w tym miejscu powiedzieć trochę o kolejności wykonywanych operacji w konstruktorze. Zacznijmy od przypadku bez dziedziczenia: mamy klasę A, która nie dziedziczy po żadnej innej klasie, ale ma jakieś pola składowe. Konstruktory działają w taki sposób, że gdy docieramy do klamerki otwierającej ich implementacje, to wszystkie pola składowe już muszą być skonstruowane, nawet jeśli zaraz w tymże konstruktorze nadamy im jakieś wartości. Powoduje to pewien problem, jeśli przynajmniej jedno z pól składowych nie posiada konstruktora zeroargumentowego (lub więcejargumentowego, ale z odpowiednimi argumentami domyślnymi). Nie możemy bowiem ominąć konstrukcji zmiennej składowej i jakoś ręcznie skonstruować ją wewnątrz konstruktora A: 001 class MojaLiczba { 002 public: 003 int x; 004 005 MojaLiczba(int a) 006 { 007 a = x; 008 } 009 }; 010 011 class KlasaLiczbowa { 012 MojaLiczba ml; 013 014 KlasaLiczbowa(int a) 015 { //błąd: w tym miejscu MojaLiczba ml musi już być zbudowane 016 ml.x = a; 017 } 018 }; Jak widać na przykładach, listę inicjalizacyjną budujemy po napisaniu sygnatury konstruktora, ale przed klamerką rozpoczynającą implementację. Dodajemy dwukropek, a następnie wpisujemy nazwę pola składowego klasy (rzeczywistą nazwę pola, nie typ) i w nawiasie argumenty do konstruktora tego pola. Poniższy fragment wykonuje nieco nadmiarowej pracy, ponieważ niejako dwukrotnie ustala wartość pola int x w składowej ml: 3

001 class KlasaLiczbowa { 002 MojaLiczba ml; 003 004 KlasaLiczbowa(int a) 005 : ml(0) //legalne, ale bez sensu... 006 { 007 ml.x = a; //...bo właśnie nadpisaliśmy część pracy 008 } 009 }; Najlepiej byłoby zrobić to w ten sposób: 001 class KlasaLiczbowa { 002 MojaLiczba ml; 003 004 KlasaLiczbowa(int a) 005 : ml(a) 006 { 007 //niczego już nie musimy tutaj robić 008 } 009 }; Ważne! Zauważmy, że użycie list inicjalizacyjnych powoduje, że możemy przemieścić w klasie MojaLiczba składową x do części prywatnej tejże klasy grzebanie w tym polu składowym odbywać się teraz będzie wyłącznie z poziomu klasy MojaLiczba i reszcie świata (w tym konstruktorowi klasy KlasaLiczbowa) można odciąć dostęp. Możemy oczywiście korzystając z list inicjalizacyjnych wywoływać konstruktory wieloargumentowe. Co więcej, jeśli potrzebujemy zainicjować więcej niż jedno pole składowe, to oddzielamy wywołania poszczególnych konstruktorów pól przecinkiem. Poniżej przykład pokazujący obie techniki. 001 #include <string> 002 003 using namespace std; 004 005 struct ulamek { 006 int licznik, mianownik; 007 ulamek(int l, int m) 008 : licznik(l), mianownik(m) {} 009 }; 010 011 class MojaLiczba { 012 int x; 013 public: 014 MojaLiczba(int a) 015 : x(a) {} 016 }; 017 4

018 class DuzaKlasa { 019 string napis; 020 MojaLiczba ml; 021 ulamek ulam; 022 public: 023 DuzaKlasa(string s, int a, int licznik, int mianownik) 024 : napis(s), ml(a), ulam(licznik, mianownik) {} 025 }; 026 027 int main() 028 { 029 DuzaKlasa dk("jestem sobie DuzaKlasa", 42, 5, 7); 030 return 0; 031 } Jeśli teraz dojdzie nam dziedziczenie, to sprawa powinna już być prosta listę inicjalizacyjną należy rozpocząć (o ile jest to potrzebne/pożądane) od wywołania konstruktora klasy nadrzędnej, co też dzieje się w klasach Kot i Pies. 1.2 Prywatność a dziedziczenie W naszych zwierzęcych podklasach używamy zmiennej napisowej imie, która jest publiczna w klasie Zwierz. Co by się stało przy przemieszczeniu imienia do innej strefy dostępowej i jak się to ma do dziedziczenia? 1.2.1 private Atrybuty i metody oznaczone jako prywatne w klasie bazowej są niedostępne poza tą klasą. Aby można było odczytać imię zwierzaka z poziomu klasy pochodnej, musiałaby zostać w Zwierz zdefiniowana nieprywatna metoda, potrafiąca odczytać imię: 001 class Zwierz { 002 private: 003 string imie; 004... 005 nie-private: //np. public 006 string getimie() const 007 { 008 return imie; 009 } 010 }; 011 012 class Kot : public Zwierz { 013... 014 public: 015 string dajglos() const 016 { 017 return "jestem kotem i nazywam sie " + getimie() + "\n"; 018 } 5

019 }; Czym może być nie-private oprócz public zaraz się okaże. Tak czy inaczej dostajemy implementację zwierząt, która ustala imię w trakcie konstruowania obiektów tego typu i później już tego imienia zmienić się nie da, ale przynajmniej możemy je z dowolnego miejsca odczytywać za pośrednictwem publicznej metody getimie(). Takie zachowanie (inicjalizacja na stałe niektórych pól) czasami jest bardzo pożądane. 1.2.2 protected Brakujące ogniwo to tryb chroniony. Metody i pola chronione zachowują się jak publiczne dla klas pochodnych, ale prywatne dla reszty świata. Jeśli zatem umieścimy zmienną imie w przestrzeni chronionej, to nadal legalna będzie zupełnie pierwotna implementacja metod dajglos(): 001 #include <iostream> 002 #include <string> 003 004 using namespace std; 005 006 class Zwierz { 007 protected: 008 string imie; 009 public: 010 string dajglos() const 011 { 012 return "nie wiem czym jestem, ale nazywam sie " + imie + "\n"; 013 } 014 015 Zwierz(const string &s) 016 { 017 imie = s; 018 } 019 }; 020 021 class Kot : public Zwierz { 022 public: 023 string dajglos() const 024 { 025 return "jestem kotem i nazywam sie " + imie + "\n"; 026 } 027 028 void setimie(const string &s) 029 { 030 imie = s; //działa, bo imie jest protected 031 } 032 033 Kot(const string &s) 034 : Zwierz(s) {} 6

035 }; 036 037 class Pies : public Zwierz { 038 public: 039 string dajglos() const 040 { 041 return "jestem psem i nazywam sie " + imie + "\n"; 042 } 043 044 Pies(const string &s) 045 : Zwierz(s) {} 046 }; 047 048 int main() 049 { 050 Zwierz Z("Metan"); 051 Kot K("Etan"); 052 Pies P("Propan"); 053 054 cout << Z.dajGlos(); 055 cout << K.dajGlos(); 056 cout << P.dajGlos(); 057 058 Z.setImie("Butan"); //nie ma takiej metody! 059 Z.imie = "Pentan"; //nie działa, bo protected 060 K.setImie("Heksan"); //działa 061 K.imie = "Heptan"; //nie działa, bo protected 062 P.setImie("Oktan"); //nie ma takiej metody! 063 P.imie = "Nonan"; //nie działa, bo protected 064 065 return 0; 066 } 2 Akcesory W wielu projektach/korporacjach/czymkolwiek uważa się, że publiczne atrybuty są Złe i Obrzydliwe. Ustawia się więc wszystko co się da jako prywatne, a do atrybutów tworzy publiczne metody dostępowe w rodzaju T getcokolwiek() const i void setcokolwiek(const T &), podobnie jak powyżej zrobiliśmy string getimie() const i void setimie(const string &). Takie metody nazywa się często akcesorami. Nie znaczy to z miejsca, że publiczne atrubyty są rzeczywiście Złe, bo niektórzy tworzą akcesory do wszystkiego, niezależnie od rzeczywistej potrzeby. Tak naprawdę dostęp przez takie najprostsze akcesory niewiele się wtedy różni od zwykłego oznaczenia atrybutu jako publicznego. W jakich sytuacjach możemy zatem chcieć istotnie kontrolować dostęp do atrybutu przez akcesory? Gdy podczas przypisań albo odczytów chcemy móc wygenerować jakieś zapisy księgujące, np. wpisy do logów diagnostycznych. 7

Jeśli podczas zmian atrybutu chcemy dokonać kontroli poprawności danych. Na przykład, co jeśli w klasie ulamek zostanie wywołany zapis do mianownika z wartością 0? Jeśli pewne wewnętrzne szczegóły implementacyjne mogą ulec zmianie (np. typ atrybutu), a my musimy zachować sensowną kompatybilność (albo raczej stabilność) interfejsu w przyszłości. Jak widać w każdym z tych przypadków śledzenie wszelkich operacji na publicznych atrybutach stałoby się bólem w pewnej tylnej części ciała. Warto zatem czasami skupić logikę dostępową w jednym miejscu. 3 Polimorfizm i metody wirtualne Coś tam zaczęliśmy mówić o tym, ale wgłębimy się w temat przy najbliższej okazji, więc na razie notatek z tego nie spisuję. 8