You are on page 1of 26

Ankieta - co umiemy

Programowanie obiektowe i C++
• Programowałam/em w C++.

Janusz Jabłonowski
Wydział Matematyki, Informatyki i Mechaniki Uniwersytet Warszawski http://mst.mimuw.edu.pl

• Programowałam/em w języku obiektowym. • Programowałam/em w C. • Programowałam/em w innym języku. • Nigdy nie programowałam/em.

26 marca 2011

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

1 / 201

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

2 / 201

Sprawy organizacyjne

Co będzie na wykładach

• Rejestracja. • Godziny wykładu. • http://moodle.mimuw.edu.pl. • Co umiemy. • Co będzie na wykładach. • Zasady zaliczania.

• Podstawy obiektowości [1]. • Elementy C w C++ [2-4]. • Programowanie obiektowe w C++ (klasy, dziedziczenie, metody

wirtualne, szablony, ...) [pozostałe wykłady].
• Być może: Smalltalk, Delphi, Java, C# [tylko jeden z tych tematów i

tylko jeśli starczy czasu].

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

3 / 201

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

4 / 201

Zasady zaliczania

Czemu C++?

Zaliczenie ćwiczeń:
• obecności, • klasówka, • program zaliczeniowy. • Bardzo popularny. • Dostępny w wielu implementacjach. • Łatwo dostępna literatura.

Egzamin na koniec wykładu:
• trzeba będzie napisać fragment programu w C++, • 1,5 - 2 godz.

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

5 / 201

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

6 / 201

Zastrzeżenia

Czego nie będzie na wykładach

• C++ nie jest jedynym językiem obiektowym. • C++ nie jest najlepszym językiem obiektowym. • w C++ można pisać programy nie mające nic wspólnego z • Opisu poszczególnych implementacji C++. • Opisu poszczególnych bibliotek i rozszerzeń języka C++.

programowaniem obiektowym.
• C++ jest trudnym językiem, z bardzo rozbudowaną składnią i

subtelną semantyką.

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

7 / 201

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

8 / 201

Programowanie obiektowe - wstęp

Programowanie obiektowe - wstęp cd.

• Wszyscy o nim mówią, wszyscy go używają i nikt nie wie co to jest. • Podejście obiektowe swym znaczeniem wykracza daleko poza

• Programowanie obiektowe jest czymś więcej niż jedynie dodaniem do

programowanie (ogólnie: opis skomplikowanych systemów). • Podejście obiektowe to inny sposób widzenia świata:
• agenci do których wysyła się komunikaty, • zobowiązani do realizacji pewnych zadań, • realizujący je wg pewnych metod postępowania. Te metody są ukryte

języka programowania kilku nowych cech, jest innym sposobem myślenia o procesie podziału problemów na podproblemy i tworzeniu programów.
• Na programowanie obiektowe można patrzeć jako na symulowanie

rozważanego świata.
• W programowaniu obiektowym mamy do czynienia z zupełnie innym

przed zlecającym wykonanie zadania. • Przykład: poczta

modelem obliczeń, zamiast komórek pamięci mamy obiekty (agentów), komunikaty i zobowiązania.

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

9 / 201

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

10 / 201

Obiekt

Polimorfizm

• Obiekt ma swój własny stan i swoje własne zachowanie (operacje). • Każdy obiekt jest egzemplarzem pewnej klasy. • Zachowanie obiektu jest określone w klasie tego obiektu. • Z każdym obiektem jest związany zbiór zobowiązań (responsibilities) -

• Zachowanie obiektu można zaobserwować wysyłając do niego

komunikat.
• W odpowiedzi obiekt wykona swoją metodę, związaną z tym

komunikatem.
• To jakie akcje zostaną wykonane zależy od obiektu - obiekt innej

protokół.

klasy może wykonać w odpowiedzi na ten sam komunikat zupełnie inne akcje (polimorfizm).

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

11 / 201

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

12 / 201

Polimorfizm cd.

Programowanie obiektowe - ponowne wykorzystywanie

• Przesłanie komunikatu jest podobne do wywołania procedury, istotna

• Programowanie obiektowe polega także na tym, że staramy się co

różnica między nimi polega na tym, że to jakie akcje zostaną wykonane po przesłaniu komunikatu zależy od tego, do kogo ten komunikat został wysłany.
• Wiązanie nazwy komunikatu z realizującą go metodą odbywa się

tylko się da zrzucić na innych (na agentów), a nie staramy się wszystkiego robić samemu (nawyk z programowania imperatywnego). Umożliwia to ponowne wykorzystywanie (reuse) oprogramowania.
• ”Ważnym pierwszym krokiem w ponownym wykorzystywaniu

dopiero podczas wykonywania, a nie podczas kompilowania, programu (metody wirtualne, wczesne i późne wiązanie metod).

komponentów jest chęć spróbowania zrobienia tego.”
• Programowanie obiektowe nie ma większej mocy wyrażania, ale

ułatwia rozwiązywanie problemów w sposób właściwy dla tworzenia dużych systemów.

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

13 / 201

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

14 / 201

Programowanie obiektowe - etap ewolucji
• Programowanie obiektowe stanowi następny etap ewolucji

Programowanie obiektowe - podsumowanie

mechanizmów abstrakcji w programowaniu:
• • • • •

procedury, bloki (w sensie Smalltalka), moduły, ATD, programowanie obiektowe.

• Podsumowanie własności programowania obiektowego (Alan Kay,

1993):
• • • • • •

• W kontekście programowania obiektowego mówimy o projektowaniu

sterowanym zobowiązaniami (RDD Responsibility-Driven Design). Przerzucanie zobowiązań na innych wiąże się z dawaniem im większej samodzielności, dzięki czemu komponenty oprogramowania stają się mniej od siebie zależne, co z kolei ułatwia ich ponowne wykorzystywanie.

Wszystko jest obiektem. Obliczenie realizują obiekty przesyłając między sobą komunikaty. Obiekt ma swoją pamięć zawierającą obiekty. Każdy obiekt jest egzemplarzem klasy. Klasa stanowi repozytorium zachowania obiektu. Klasy są zorganizowane w hierarchię dziedziczenia.

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

15 / 201

J. Jabłonowski (MIM UW)

PO i C++

26 marca 2011

16 / 201

Jabłonowski (MIM UW) PO i C++ 26 marca 2011 19 / 201 J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 17 / 201 J. Użytkownika klasy interesuje tylko interfejs projektować. • W czystej postaci dziedziczenie odzwierciedla relację is-a (jest czymś). Kolejka < Lista). że oprogramowanie obiektowe składa się z wielu (w dużym stopniu) niezależnych od siebie komponentów. powoduje że tworzone zgodnie z tym podejściem oprogramowanie w naturalny sposób jest podzielone na (w dużym stopniu) niezależne od siebie komponenty. łatwo jest te komponenty ponownie wykorzystywać (reusability). pochodnych). • Modyfikacje kodu w nadklasach mają wpływ na podklasy i vice-versa korzystania z bibliotek gotowych komponentów. testować. tworzenia uniwersalnych narzędzi. modyfikować i opisywać niezależnie od reszty systemu. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 18 / 201 Zalety programowania obiektowego Zalety programowania obiektowego cd. można drzew lub DAGów dziedziczenia).tu można zastosować składanie. • Dziedziczenie pozwala pogodzić ze sobą dwie sprzeczne tendencje w tworzeniu oprogramowania: • chcemy żeby stworzone systemy były zamknięte. • rozszerzanie (Kolorowe Okno < Czarno-białe Okno). J. • Czasami chcemy wyrazić w hierarchii klas wyjątki (pingwin). J. należących do niej obiektów (komunikaty i ich znaczenie). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 21 / 201 J. a nie zawartość tych obiektów (metody i dane). J. • Dane i zachowanie związane z klasami z wyższych poziomów tej to uzyskać dzięki przedefiniowywaniu (podmienianiu) metod (method overriding). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 20 / 201 Zalety dziedziczenia Zalety dziedziczenia cd. • Zasada podstawialności: zawsze powinno być możliwe podstawienie hierarchii są dostępne w klasach po nich dziedziczących (pośrednio lub bezpośrednio). rzadko kiedy takie narzędzia są równie efektywne. • Mówimy o nadklasach (klasach bazowych) i podklasach (klasach obiektów podklas w miejsce obiektów nadklas. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 24 / 201 . • Fakt. • Jest to niezwykle ważna zaleta. się błędy w częściej używanych fragmentach programów). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 22 / 201 Wady: narzuty związane z programowaniem obiektowym • Szybkość wykonywania (programowanie obiektowe zachęca do Narzuty związane z programowaniem obiektowym cd. komunikatu z metodą odbywa się dopiero podczas wykonywania programu). implementować. konkretnego zastosowania). • Klasy obiektów można kojarzyć w hierarchie klas (prowadzi to do Dziedziczenie cd. • chcemy żeby stworzone systemy były otwarte. że w podejściu obiektowym każdy obiekt jest całkowicie odpowiedzialny za swoje zachowanie. Bardzo często ta relacja jest mylona z relacją has-a (ma coś) dotyczącą składania. • ograniczanie (tak nie należy projektować .Dziedziczenie • Jedna z fundamentalnych własności podejścia obiektowego. ich zobowiązań i przesyłanych między nimi komunikatów. • Narzut związany z przekazywaniem komunikatów (wiązanie (wirtualność metod). korzystanie z takich bibliotek może zwiększać rozmiar programów). J. • Ponowne wykorzystywanie zwiększa niezawodność (szybciej wykrywa przodku). • specyfikacja (klasy abstrakcyjne). co narzędzia stworzone do jednego. z pominięciem tego jak są realizowane obsługujące te komunikaty metody • Ukrywanie informacji. • Zgodność interfejsów (gdy wiele klas dziedziczy po wspólnym • Możliwość ponownego wykorzystywania (nie trzeba od nowa pisać odziedziczonych metod i deklaracji odziedziczonych zmiennych). Ten mechanizm nazywamy kapsułkowaniem (lub hermetyzacją). gdyż takie komponenty można • Tworzenie oprogramowania w metaforze porozumiewających się między sobą agentów skłania do bardziej abstrakcyjnego myślenia o programie: w kategoriach agentów. • Dzięki temu. • Możliwe zastosowania dziedziczenia: • specjalizacja (Kwadrat < Prostokąt). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 23 / 201 J. • Ponowne wykorzystywanie pozwala szybciej tworzyć nowe systemy (budować je z klocków). • Rozmiar programów (programowanie obiektowe zachęca do • Efekt jo-jo (nadużywanie dziedziczenia może uczynić czytanie programu bardzo żmudnym procesem).

to po jej nazwie jest napis konstrukcji składniowej. to po jej nazwie jest napis opc (umieszczony jako indeks). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 27 / 201 J. f. np. (umieszczony jako indeks). • Nowy standard C++0x (rok publikacji nadal nieznany). • Elementy opisujące konstrukcje języka zapisano pismem pochyłym. bez pogrubienia (np. • C++ (j.k. Kristen Nygaard. 1972). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 28 / 201 Instrukcja wyrażeniowa instrukcja wyrażeniowa: wyrażenieopc . J. // poprawne int f=f. będzie tu omawiany. 1 • Jeżeli dana konstrukcja może wystąpić w danym miejscu raz lub więcej razy. {).. Ritchie. • Deklaracja też jest instrukcją. • Instrukcje etykietowane case i default mogą wystąpić jedynie wewnątrz instrukcji wyboru. • Uwaga: to nie jest opis języka C! • C++ jest kompilowanym językiem ze statycznie sprawdzaną 1979-80). • Wyrażenie stałe stojące po etykiecie case musi być typu całkowitego. do zapisania pustej treści instrukcji pętli. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 30 / 201 Instrukcja złożona (blok) Przykład przesłaniania blok: instrukcja0 struct ff{int k. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 31 / 201 J. • Zwykle instrukcjami wyrażeniowymi są przypisania i wywołania funkcji. separatory) zapisano są pismem Instrukcje języka C++ prostym.k++. J. to po jej nazwie jest napis (umieszczony jako indeks).w.} f. 1990). • Nie ma żadnych separatorów oddzielających poszczególne instrukcje. wyrażenie). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 25 / 201 J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 26 / 201 Notacja • Elementy języka (słowa kluczowe. J. 1967). New Jersey.. Elementy C w C++ Norweski Ośrodek Obliczeniowy w Oslo. • C z klasami (Bjarne Stroustrup. • C (Dennis M. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 32 / 201 . 1983). C++. Instrukcja etykietowana • Efektem jej działania są efekty uboczne wyliczania wartości wyrażenia instrukcja etykietowana: identyfikator : instrukcja case stałe wyrażenie : instrukcja default : instrukcja • Pierwszy rodzaj instrukcji etykietowanej dotyczy instrukcji goto i nie (sama wartość po jej wyliczeniu jest ignorowana). • Komisja X3J16 powołana do standaryzacji C++ przez ANSI (ANSI zgodnością typów. Bell Laboratories . New Jersey.: i = 23∗k +1. wypisz dane(Pracownik). znany jako C++98 (1998). • Instrukcja złożona wyznacza zasięg widoczności. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 29 / 201 J. • Simula 67 (Ole-Johan Dahl. // poprawne • Służy do zgrupowania wielu instrukcji w jedną.. Bjorn Myhrhaug. Bell Laboratories. • Program w C++ może się składać z wielu plików (zwykle pełnią one rolę modułów). pogrubionym (np. instrukcja: instrukcja instrukcja blok instrukcja instrukcja instrukcja instrukcja instrukcja instrukcja etykietowana wyrażeniowa warunkowa wyboru pętli deklaracyjna próbuj skoku • Poszczególne wiersze odpowiadają poszczególnym wersjom omawianej J. • Jeżeli dana konstrukcja może w danym miejscu wystąpić lub nie.Historia C++ • Algol 60 (13-to osobowy zespół. // niedozwolone f++. • Szczególnym przypadkiem jest instrukcja pusta: . użyteczna np. //. • Standard C++ ISO/IEC 14882:1998. 0 • Jeżeli dana konstrukcja może w danym miejscu wystąpić 0 lub więcej razy. 1960-63).

• Podczas wykonywania instrukcji switch oblicza się wartość wyrażenia. jak zasięg nazw zadeklarowanych w inst ini for. J. jeśli jest etykieta default. • Instrukcja może być deklaracją (jej zasięgiem zawsze jest tylko ta instrukcja). bo i tak zawsze najpierw są analizowane etykiety case. • Podinstrukcje (wewnątrz instrukcji) mogą być etykietowane jedną (kilkoma) etykietami przypadków: case wyrażenie stałe : • Wszystkie stałe przypadków muszą mieć różne wartości. której zasięgiem jest dana instrukcja. • Instrukcja może być deklaracją. to wartość różna od zera jest interpretowana jak true. • Warunek może być także deklaracją (z pewnymi ograniczeniami) mającą część inicjującą. • Jeśli wartość wyrażenia jest liczbą lub wskaźnikiem. • Warunek w pętli while może być także deklaracją (z pewnymi ograniczeniami). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 37 / 201 instrukcja). • Pominięcie warunku jest traktowane jako wpisanie true. Jeśli któraś z nich równa się wartości warunku. W pętli while nie można pominąć warunku. J. to sterowanie jest przekazywane do instrukcji poprzedzonej etykietą 26 marca 2011 34 / 201 z tą wartością. (jej zasięgiem zawsze jest tylko ta instrukcja). J. sterowanie przechodzi bezpośrednio za instr. • Instrukcja składowa może być deklaracją (jej zasięgiem zawsze jest tylko ta instrukcja składowa). switch. wpp. której zasięgiem jest ta pętla. W C++ zawsze przy wychodzeniu z zasięgu widoczności następuje niszczenie obiektów automatycznych zadeklarowanych w tym zasięgu. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 38 / 201 Semantyka pętli for Instrukcje skoku Instrukcja for jest (praktycznie) równoważna instrukcji: { inst ini for while ( warunek ) { instrukcja wyrażenie . • Warunek wylicza się po każdym wykonaniu instrukcji. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 33 / 201 Pętle while i do instrukcja pętli: while (warunek) instrukcja do instrukcja while (warunek). poprzedzonej tą etykietą. jej zasięgiem jest cała instrukcja warunkowa. J. a wartość równą zeru za warunek fałszywy. a następnie porównuje się ją ze wszystkimi stałymi występującymi po case. } } • break. Różnica: jeśli w instrukcji wystąpi continue. Instrukcja while • Wartość wyrażenia jest niejawnie przekształcana na typ całkowity lub • Dopóki warunek jest spełniony. • Ta deklaracja musi zawierać część inicjalizującą. wyliczeniowy. J. • Wyrażenie musi być typu całkowitego. • Wartość warunku typu innego niż logiczny jest niejawnie przekształcana na typ bool. • Może wystąpić (co najwyżej jedna) etykieta: default : Nie musi być ostatnią etykietą. • Zawsze wykonuje co najmniej jeden obrót. J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 40 / 201 . Jabłonowski (MIM UW) PO i C++ wpp.for pętla for: for (inst ini for warunekopc . za false. arytmetycznego lub Instrukcja wyboru instrukcja wyboru: switch (wyrażenie) instrukcja • Powoduje przekazanie sterowania do jednej z podinstrukcji wskaźnikowego.Instrukcja warunkowa instrukcja warunkowa: if (warunek) instrukcja if (warunek) instrukcja else instrukcja warunek: wyrażenie • Wyrażenie musi być typu logicznego. o etykiecie wyznaczonej przez wartość wyrażenia. • Może nie wykonać ani jednego obrotu. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 35 / 201 J. • Ta deklaracja musi zawierać część inicjującą. to wartość różną od zera uważa się za warunek prawdziwy. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 39 / 201 J. Instrukcja do • Powtarza wykonywanie instrukcji aż warunek przestanie być spełniony. wyraenieopc ) instrukcja inst ini for: instrukcja wyrażeniowa prosta deklaracja • Warunek jak w poprzednich pętlach. Przy każdym obrocie pętli sterowanie wchodzi i opuszcza ten lokalny zasięg. prawie zawsze trzeba jawnie kończyć wykonywanie obsługi przypadku instrukcją break!. • Warunek wylicza się przed każdym wykonaniem instrukcji. • return wyrażenieopc . to zasięg zadeklarowanych • Warunek musi być typu logicznego. • Wartość warunku jest niejawnie przekształcana na typ bool. arytmetycznego lub wskaźnikowego. • Wyrażenie do cd Pętle while i może być także deklaracją (z pewnymi ograniczeniami). • Zasięg nazw zadeklarowanych w warunku jest taki sam. • else dotyczy ostatnio spotkanego if bez else. do instr. to wyrażenie w pętli for będzie obliczone przed obliczeniem warunku. • Jeśli instrukcja instr inic jest deklaracją. • goto identyfikator . wykonuje podaną instrukcję. Przy każdym obrocie pętli sterowanie wchodzi i opuszcza ten lokalny zasięg. • continue. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 36 / 201 Pętle while i do cd Instrukcja pętli . że w pętli do warunek nie może być deklaracją. Ponieważ sterowanie przekracza etykiety case i default. wpp. pętla for warunek: wyrażenie występujących w instrukcji. w kolejności odwrotnej do ich deklaracji. • Instrukcja może być deklaracją (jej zasięgiem zawsze jest tylko ta nazw sięga do końca pętli. • Jeśli wartość warunku jest liczbą lub wskaźnikiem. • Zauważmy. z wszelkimi tego konsekwencjami.

• Deklaracja określa typ. unsigned long int (czyli nigdy nie są typu unsigned int!). J. • Deklarując nazwę w C++ można podać specyfikator klasy pamięci: • auto prawie nigdy nie stosowany jawnie (bo jet przyjmowany domyślnie).w. z dodatkowym wskazaniem dla kompilatora. ale można jej używać jako stałej (oznaczającej pusty wskaźnik) dowolnego typu wskaźnikowego. J. L i l do jawnego zapisywania stałych bez znaku i stałych long. • Sterowanie przechodzi bezpośrednio za przerwaną instrukcję. • Jeśli typem wyniku funkcji jest void. Te komentarze nie mogą się zagnieżdżać. • Ten identyfikator może przesłonić jakiś identyfikator z bloku zewnętrznego (do końca tego bloku). w którym jest zadeklarowany. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 46 / 201 • Wprowadza do bloku nowy identyfikator. jego typ. Ich typem jest pierwszy z wymienionych typów. • Może się pojawić jedynie wewnątrz instrukcji pętli i powoduje zakończenie bieżącego obrotu (najciaśniej otaczającej) pętli. • static to słowo ma kilka różnych znaczeń w C++. long int. że deklarowana zmienna będzie często używana. W C++ mamy dwa rodzaje komentarzy: • Komentarze jednowierszowe zaczynające się od //. • Każda funkcja o typie wyniku innym niż void musi zawierać co najmniej jedną taką instrukcję. unsigned long int. u. • Stała 0 jest typu int. J. a definicja (czyli miejsce gdzie została przydzielona pamięć) jest gdzie indziej. J. po dotarciu sterowania do końca treści funkcji. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 43 / 201 J. • Inicjowanie zmiennych (auto i register) odbywa się przy każdym wykonaniu ich instrukcji deklaracji. może też określać wartość początkową. w którym dają się reprezentować: int. J. że identyfikator pochodzi z innego pliku. zaczynające się od /∗ i kończące ∗/. • register tyle co auto. w którym dają się reprezentować: int. Typ j. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 45 / 201 Komentarze Literały całkowite • Dziesiętne (123543). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 44 / 201 Instrukcja deklaracji Deklaracje • Każdy identyfikator musi być najpierw zadeklarowany.Instrukcja break Instrukcja continue • Może się pojawić jedynie wewnątrz pętli lub instrukcji wyboru i powoduje przerwanie wykonywania najciaśniej ją otaczającej takiej instrukcji. wówczas koniec działania funkcji następuje • Nie używamy tej instrukcji. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 47 / 201 J. 0x4A). w którym dana wartość się mieści. przy czym znów jest wybierany najmniejszy typ (zgodny z przyrostkiem). definiuje treść funkcji). Ich typem jest pierwszy z wymienionych typów. Zmienne te giną przy wychodzeniu z bloku. long int. • Komentarze (być może) wielowierszowe. unsigned int. • Szesnastkowe (0x3f. tu oznacza. czyli w tym miejscu jest tylko jego deklaracja (żeby kompilator znał np. to funkcja może nie zawierać żadnej instrukcji return. • extern oznacza. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 41 / 201 J. • Ósemkowe (0773). • Przyrostki U. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 48 / 201 . instrukcja deklaracji: blok deklaracji • Zwykle deklaracja jest też definicją (przydziela pamięć zmiennej. że identyfikator będzie zachowywał swoją wartość pomiędzy kolejnymi wejściami do bloku. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 42 / 201 Instrukcja return Instrukcja goto • Służy do kończenia wykonywania funkcji i (ewentualnie) do przekazywania wartości wyniku funkcji.

• Przyrostek f. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 52 / 201 Identyfikatory Co można zrobić z typami? • Identyfikator (nazwa) to ciąg liter i cyfr zaczynający się od litery ( traktujemy jako literę). oto niektóre z nich: • \n (nowy wiersz). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 55 / 201 J. można podać od jednej do trzech cyfr ósemkowych). Liczby całkowite bez znaku: • unsigned char. • Nazw zaczynających się od i dużej litery. • Operacje dozwolone na nazwach typów: • podawanie typu innych nazw. F (float). Takich sekwencji jest 13. Liczby całkowite: • char. • specyfikowanie jawnych konwersji. • Słowo kluczowe C++ nie może być nazwą. • Długość nazwy nie jest ograniczona przez C++ (może być • Typ określa rozmiar pamięci. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 53 / 201 J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 56 / 201 . • true. (część int można opuścić) J. można podać jedną lub więcej cyfr szesnastkowych). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 51 / 201 J. • false. L (long double). mamy tu statyczną kontrolę typów.. 12. • Nazwa to maksymalny ciąg liter i cyfr. • \’ (apostrof). • signed char. • Niektóre znaki są opisane sekwencjami dwu znaków zaczynającymi się od \. • . • Rozróżnia się duże i małe litery. • int (signed int). • \ooo (znak o ósemkowym kodzie ooo.223e3.Literały zmiennopozycyjne Literały znakowe (typu char) • Mają typ double (o ile nie zmienia tego przyrostek) • 1. sąsiednie stałe napisowe (nawet z różnych wierszy) są łączone. • unsigned int. Każda z tych sekwencji opisuje pojedynczy znak! J.. bądź zawierających (podwójne podkreślenie) nie należy definiować samemu (są zarezerwowane dla implementacji i standardowych bibliotek). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 49 / 201 J. • Zakończony znakiem ’\0’. • short int (signed short int). • new. -35E-11. J. • sizeof. • long int (signed long int).. dozwolone operacje i ich znaczenie. • Z każdą nazwą w C++ związany jest typ. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 54 / 201 Typy podstawowe Typy podstawowe cd. • \\ (lewy ukośnik). l. • Znak umieszczony w apostrofach (’a’). • Musi się zawierać w jednym wierszu. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 50 / 201 Literały napisowe (typu const char[n]) Literały logiczne (typu bool) • Ciąg znaków ujęty w cudzysłów (”ala\n”). • unsigned short int. • unsigned long int. J. • Typy można nazywać. • \xhhh (znak o szesnastkowym kodzie hhh. ale ..23. ograniczona przez implementację).

gdzie nie jest wymagana znajomość rozmiaru struktury. • char może być typem ze znakiem lub bez znaku. • Typ referencyjny zapisujemy jako T&. • Tablice wielowymiarowe deklaruje się wypisując kilka razy po sobie [rozmiar] (nie można zapisać tego w jednej parze nawiasów definiuje się mniej wygodnie.wskaźniki • Wskaźniki są bardzo często używane w C++. typedef pozwala ominąć tę niedogodność. jeśli mamy strukturę. J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 57 / 201 J. short 16. • Dwa niezależnie zadeklarowane typy są różne. • Struktura może być wynikiem funkcji. • Właściwie nie ma tablic: a[i] oznacza ∗(a+i) co z kolei oznacza i[a].Typy podstawowe cd. • C++ gwarantuje.referencje części wykładu). nią przypisywać. }. okrągłego nawiasu. Przekazywanie tablicy jako parametru oznacza przekazanie adresu pierwszego elementu. p2 to wskaźniki. Składniowo ma ona postać zwykłej deklaracji poprzedzonej słowem kluczowym typedef. p1. short. // Błędny fragment programu (ale kompilujący się bez // ostrzeżeń) zwn złe położenie prawego. Jabłonowski (MIM UW) struct <nazwa>. • Zwn. • char ma co najmniej 8. • Można definiować struktury wzajemnie odwołujące się do siebie. J. • W C++ sizeof(char) wynosi 1 (z definicji). używać tak.struktury • Struktura to zestaw elementów dowolnych typów (przynajmniej w tej Typy pochodne . • −> jeśli mamy wskaźnik do struktury. p+wyr. • Typ wartości logicznych bool. a wyr to wyrażenie całkowitoliczbowe. parametrem funkcji i można na • Referencja (alias) to inna nazwa już istniejącego obiektu. • Odwołanie do elementu tablicy wymaga użycia operatora []. Liczby rzeczywiste: • float. Ale uwaga na różnicę: int ∗p. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 59 / 201 J. • Referencja musi być zainicjalizowana i nie można jej zmienić.. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 62 / 201 Tak wstępnie zadeklarowanej struktury można używać tyko tam. kwadratowych). int lub long. a nie samej referencji! • Referencje są szczególnie przydatne dla parametrów funkcji (przekazywanie przez zmienną). oznacza coś zupełnie innego niż J. Używa się do tego deklaracji: J. strcpy(dest. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 58 / 201 Typy pochodne . • Struktury zapisujemy następująco: struct <nazwa> { typ 1 pole 1. • double. Typy podstawowe cd.: char ∗dest = new char[strlen(src+1)]. • * (operator wyłuskania). • Wskaźnik do typu T deklarujemy (zwykle) jako T*. • long double. np. gdzie T jest jakimś typem (T nie może być typem referencyjnym). składnię C++ (wziętą z C) wskaźniki do funkcji i tablic Typy pochodne . PO i C++ 26 marca 2011 61 / 201 J. a long 32 bity. Oznacza ona (stały) wskaźnik do pierwszego elementu tablicy. • Nie jest natomiast zdefiniowane porównywanie struktur (== i !=). • Uwaga na wskaźniki . • p++. typ 2 pole 2. Jabłonowski (MIM UW) int p[100]. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 63 / 201 J. że • 1 = sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) • sizeof(float) <= sizeof(double) <= sizeof(long double) • sizeof(T) = sizeof(signed T) = sizeof(unsigned T). • typedef służy do zadeklarowania identyfikatora. • W C++ nazwy tablicy można używać jako wskaźnika. indeksowaną od 0 do rozmiar-1. którego można potem • Można definiować wyliczenia np. • Do pól struktury odwołujemy się za pomocą: • .tablice • T[rozmiar] jest tablicą rozmiar elementów typu T. p1-p2 gdzie p. Definiowanie nazwy typu Wyliczenia • Deklaracja typedef służy do nazywania typu. dla T = char. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 64 / 201 . p-wyr.: enum kolor{ czerwony. • Nie ma operacji przypisania tablic (przypisanie kopiuje tylko wskaźniki). zielony }. .tu bardzo łatwo o błędy. • Operacje na wskaźnikach: • przypisanie. nawet jeśli mają identyczną strukturę. PO i C++ 26 marca 2011 60 / 201 Typy pochodne . jak gdyby był nazwą typu. • stała NULL.. src). typ k pole k. • Wszelkie operacje na referencji (poza inicjalizacją) dotyczą obiektu na który wskazuje referencja.

• Jeśli parametrem jest wskaźnik. float b=2. J. f(−1. } // Wszystkie poniższe wywołania odnoszą się do tej samej funkcji f. może być wywołana z jednym argumentem lub bez argumentu. f(−1. 3. a nie zmiennej (oczywiście taka deklaracja musi zawierać inicjację). dzięki czemu uzyskujemy deklarację stałej. że na pewno w każdym wywołaniu da się obliczyć domyślną wartość argumentu). używając operatora &. int g(int x = f(a)). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 65 / 201 J. Przykład zastosowania dodefiniowywania wartości domyślnych poza definicją funkcji: funkcja z innego modułu używana w danym module z domyślną wartością (np. zatem funkcja z jednym parametrem. czyli g(f(2)) ! } } J. char const ∗p = ”ala”. • Lista parametrów to ciąg (oddzielonych przecinkami) deklaracji parametrów. ale jej typem jest (tylko) funkcja jednoargumentowa (bezargumentowa już nie). wskaźnik do znaku (napis). że jeśli zadeklarujemy wartość domyślną dla jednego parametru. char ∗ const p = ”ala”. J. char∗ b. Wartości domyślne parametrów cd Uwagi techniczne: Wiązanie nazw i kontrola typów wyrażenia określającego wartość domyślną odbywa się w punkcie deklaracji. Deklaruje się w ten sposób domyślną wartość argumentu odpowiadającego temu parametrowi. // argument domyślny: f(::a) void h() { a = 2. float. f(). postaci (w uproszczeniu): typ nazwa • Parametry są zawsze przekazywane przez wartość (ale mogą być referencjami lub wskaźnikami). • Nie mogą używać parametrów formalnych funkcji (bo te wyrażenia wylicza się przed wejściem do funkcji. że funkcja nie Wartości domyślne parametrów Deklarując parametr funkcji (lub metody). float=2. przy każdym wywołaniu podane wyrażenie będzie wyliczane. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 71 / 201 J. ale muszą to być ostatnie parametry. „Urszula”}. Oznacza to. int c=3) { cout << endl << a << ” ” << b << ” ” << c. sqrt(double = 2. } char∗ p = DajTablicę(100). PO i C++ 26 marca 2011 69 / 201 Nie Jabłonowski ponownie zdefiniować argumentu domyślnego w dalszej można (MIM UW) deklaracji (nawet z tą samą wartością). 3}. to jako argument można przekazać adres obiektu. a porządek wartościowania argumentów funkcji nie jest ustalony (zależy od implementacji)). g(). int f(int). • Gdy nie uda się przydzielić pamięci zgłasza wyjątek (bad alloc). int x[] = {1. • Operator new • new nazwa typu lub • new nazwa typu [ wyrażenie ]. wskaźnik do stałych znaków (stały napis). S s = {1. f(−1).0)). stały wskaźnik do znaku (napis). 7} } char ∗p = ”ala”. 2. // Tablica 100−elementowa char∗ q = DajTablicę(). { int a = 3. stały wskaźnik do stałych znaków (stały napis). {3. • Operator przeciążony nie może mieć argumentów domyślnych. 5. W tym drugim przypadku. co oznacza. char const ∗ const p = ”ala”. // Tablica 10−elementowa przekazuje wyniku (jest procedurą). int = 3). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 70 / 201 Wartości domyślne parametrów cd Wyrażenia określające wartości domyślne: Zarządzanie pamięcią • Nie mogą zawierać zmiennych lokalnych (to naturalne. float y[4] [3] = { { 1.Kwalifikator const Inicjowanie Do deklaracji dowolnego obiektu można dodać słowo kluczowe const. J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 67 / 201 J. 4. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 72 / 201 . ) instrukcja złożona • Jako typ wyniku można podać void. J. to wówczas dla wszystkich następnych parametrów również należy określić domyślne wartości (w tej lub jednej z poprzednich deklaracji funkcji): void f(int. można po jego deklaracji dopisać znak = i wyrażenie. zaś wartościowanie w każdym punkcie wywołania: // Przykład z książki Stroustrupa int a = 1. 5}.}. • Argument domyślny nie stanowi części specyfikacji typu funkcji. 6}. int). { 2. void f(int. który jednocześnie ma podaną wartość domyślną. nazwy globalne.−2. // g(f::a). void f(int a=1. • Operator delete • delete wskaźnik lub • delete[] wskaźnik. • Można używać const przy deklarowaniu wskaźników: • • • • Deklarując zmienne można im nadawać wartości początkowe: struct S {int a. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 68 / 201 Wartości domyślne parametrów cd Można w jednej funkcji zadeklarować kilka parametrów o wartościach domyślnych.−3). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 66 / 201 Funkcje • Deklaracja funkcji ma następującą postać (w pewnym uproszczeniu): typ wyniku nazwa ( lista par. int c) // Oczywiście można było od razu napisać: // void f(int a=1. Wcześniej zadeklarowane parametry formalne są w zasięgu i mogą przesłonić np. Pozwala to wywoływać tak zadeklarowaną funkcję zarówno z tym argumentem jak i bez niego. float b. a uzyskana w ten sposób wartość będzie traktowana jako brakujący argument: char∗ DajTablicę(unsigned rozmiar = 10){ return new char[rozmiar].−2). chcemy w prosty sposób zapewnić.

• sizeof(char) = 1 • wynik jest stałą typu size t zależnego od implementacji. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 76 / 201 Program • Program składa się z jednostek translacji. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 73 / 201 J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 74 / 201 Operatory Preprocesor • ∗. • =. • +. • Program składa się z: • deklaracji globalnych (zmienne..liczby zespolone Przykład klasy .. char∗ argv[]){ /∗. >. im. ∗/}. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 78 / 201 Przykład klasy .. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 75 / 201 J. • <. Najlepiej unikać J. Dokładnie to samo można wyrazić używając klas: class Zespolona{ public: double re. }.. typy) • definicji funkcji paczką kilku różnych zmiennych. +=. %=. >>. im.. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 79 / 201 J. +=. %. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 80 / 201 . wartością jest rozmiar wartości wyr.Zarządzanie pamięcią cd Jawna konwersja typu • Operator sizeof • sizeof wyr • podanego wyrażenia się nie wylicza. • Wartości tak zdefiniowanego typu nazywamy obiektami. −=. • ||.. /.”. J. >=. • ? :.. • #include <. }. Jej typem wyniku jest int. stałe. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 77 / 201 J.liczby zespolone cd Bez klas byłoby tak: struct Zespolona{ double re.. czyli kompilowane w tym samym czasie. • sizeof ( typ ) • rozmiar typu typ. • &&. • #include ”. specyfikator klasy: nagłówek klasy { specyfikacja skadowychopt } nagłówek klasy: słowo kluczowe klasy identyfikatoropc klauzula klas bazowychopc słowo kluczowe klasy specyfikator zagnieżdżonej nazwy identyfikatorop J. ∗/}. Jednostka translacji to Klasy . • Składnia deklaracji klasy: • Wśród funkcji musi się znajdować funkcja main(). • <<.. <=. J. • Jednostki translacji składające się na jeden program nie muszą być • Klasa jest nowym typem danych zdefiniowanym przez użytkownika. • int main(int argc. • ==. • Najprostsza klasa jest po prostu strukturą (rekordem w Pascalu). !=.podstawowe pojęcia pojedynczy plik źródłowy (po uwzględnieniu dyrektyw preprocesora: #include oraz tych dotyczących warunkowej kompilacji). −. /=. Obie poniższe definicje funkcji main są dopuszczalne (i żadne inne): • int main(){ /∗.>.

double modul(). • Nie podaliśmy (jeszcze) ani treści operacji.im∗z. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 82 / 201 Operacje w klasie cd Implementacja operacji z klasy Zespolona Zespolona::dodaj(Zespolona z){ Zespolona wyn. }. bo: Przy poprzedniej definicji klasy Zespolona. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 87 / 201 J. Zespolona odejmij(Zespolona). wyn. im. wyn.im+z. Zatem teraz mamy następującą deklarację: class Zespolona{ private: // tu można pominąć private: double re. Zespolona odejmij(Zespolona). J. } Zauważmy. wyn.. double mod. czyli ogólnodostępna (public:). • Projektując klasę.im. żeby użytkownik mógł bez naszej wiedzy modyfikować jej zawartość (przykład ułamek: nie chcielibyśmy.im + z2. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 88 / 201 .re∗z. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 85 / 201 J. double modul().re = re − z. To bardzo źle. można było pisać następujące instrukcje: Zespolona z. } Operacje w klasie W C++ możemy powiązać definicję typu danych z dozwolonymi na tym typie operacjami: class Zespolona{ public: double re.im. } double Zespolona::modul(){ return sqrt(re∗re + im∗im).re + z2. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 83 / 201 J. czym tak na prawdę jest typ Zespolona (trzeba przeczytać cały program. mod = sqrt(z. To co podaliśmy powyżej jest specyfikacją interfejsu typu Zespolona. J. im. return wyn. Oczywiście trzeba też określić implementację (gdzieś dalej w programie). J. Każda składowa klasy (zmienna lub metoda) może być: • Prywatna (private:). public: Zespolona dodaj(Zespolona). żeby znaleźć wszystkie definicje dotyczące liczb zespolonych).im = im − z. że wszystkie operacje na typie danych są zdefiniowane tylko w jednym miejscu.Przykład klasy . że: • Zwiększyła się czytelność programu: od razu widać wszystkie operacje dostępne na naszym typie danych.im = im + z. . wyn. że trudno się zorientować. Zespolona z2){ Zespolona wyn. • Zmieniła się liczba parametrów operacji. }. J. Możemy je zdefiniować tak: Zespolona dodaj(Zespolona z1. return wyn. zwykle nie chcemy.im. Domyślnie wszystkie składowe klasy są prywatne. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 84 / 201 Klasy powinny chronić swoje dane Po co jest potrzebna ochrona danych Nie znamy na razie metody zmuszającej użytkownika do korzystania tylko z dostarczonych przez nas operacji..im = z1. return wyn. pola re nie możemy teraz nazwać czesc rzeczywista).re. Jest to zresztą (poza domyślnym trybem dziedziczenia i oczywiście słowem kluczowym) jedyna różnica pomiędzy klasami a strukturami w C++.re = re + z. // Błąd !!! • Upada poprzednio postawiona teza. wyn.. wyn. potrzebujemy operacji na tym typie danych. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 86 / 201 Składowe prywatne i publiczne Klasa Zespolona z ochroną danych Na szczęście w C++ możemy temu bardzo łatwo zaradzić. zaś wszystkie składowe struktury publiczne. • Publiczna.re.im). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 81 / 201 J. Zespolona dodaj(Zespolona).liczby zespolone cd Ale taka definicja nie wystarcza. } Zespolona Zespolona::odejmij(Zespolona z){ Zespolona wyn. Ma to jednak tę wadę.re. ani nazw parametrów.re = z1.. żeby ktoś wpisał nam nagle mianownik równy zero). • Program użytkownika odwołujący się do wewnętrznej reprezentacji klasy niepotrzebnie się od niej uzależnia (np. • Użytkownik pisząc swoje operacje może (tak jak w przykładzie z mod) napisać je źle.

Jabłonowski (MIM UW) = z.. im.. J.modul(). double modul().im).. J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 91 / 201 • Konstruktor domyślny: • jeśli nie zdefiniujemy żadnego konstruktora. • jest konieczny. Konstruktor może mieć (nie musi) parametry. Użytkownik może natomiast napisać: mod = z. Nie można przekazać z niego wyniku instrukcją return. ale jeśli zmienna z nie jest potrzebna. że obiekt będzie miał przydzieloną pamięć (to jest Rodzaje konstruktorów Wyróżnia się kilka rodzajów konstruktorów: • Konstruktor bezargumentowy: • można go wywołać bez argumentów. • ten konstruktor nie inicjuje składowych typów prostych. taki konstruktor jest zdefiniowany.konsekwencje Czy chcemy mieć niezainicjowane obiekty? Czy chcemy mieć niezainicjowane obiekty? Oczywiście nie: Teraz zapis: mod = sqrt(z. J. Zespolona::Zespolona(double r. Konstruktor jest odpowiedzialny za dwie rzeczy: • zapewnienie. Zespolona odejmij(Zespolona)..im.re. public: // konstruktory Zespolona(double. Można w nim wywoływać funkcje składowe klasy.. Jak temu zaradzić? Można dodać metodę ini(). Zespolona odejmij(Zespolona). Konstruktor jest specjalną metodą klasy. reszta definicji J. realizuje je treść konstruktora).re∗z. }.4) ). • inicjację obiektu (to nasze zadanie. to kompilator sam wygeneruje konstruktor domyślny (bezargumentowy). J. • dla składowych będących klasami lub strukturami wywołuje ich konstruktory bezargumentowe. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 95 / 201 26 marca 2011 96 / 201 . Dalej nie ma możliwości zagwarantowania. to nic nie daje. Uwaga: automatycznie wygenerowany konstruktor kopiujący kopiuje obiekt składowa po składowej. Zespolona(Zespolona&). // Błąd! Nie ma już konstruktora domyślnego Zespolona z(3. Można w ten sposób tworzyć obiekty tymczasowe: double policz cos(Zespolona z){ // . double)..Klasa Zespolona z ochroną danych . im = i.im∗z. sprawa kompilatora). generowanie konstruktora domyślnego kończy się błędem kompilacji. // operacje Zespolona dodaj(Zespolona). że zmienna typu Zespolona będzie zainicjowana przed pierwszym jej użyciem. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 93 / 201 Konsekwencje zdefiniowania konstruktora Jakie są konsekwencje zdefiniowania konstruktora? Zespolona z. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 94 / 201 Konstruktory a obiekty tymczasowe Każde użycie konstruktora powoduje powstanie nowego obiektu. Gdybyśmy jednak chcieli. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 89 / 201 J.2). Ma taką samą nazwę jak klasa. double i){ re = r. • Konstruktor kopiujący: • można go wywołać z jednym argumentem tej samej klasy. Zespolona::Zespolona(const Zespolona& z){ re = z. więc zwykle się nie nadaje dla obiektów zawierających wskaźniki!!! J. // Wypisze się coś bez sensu } jest niepoprawny. im. Utworzony w ten sposób obiekt tymczasowy będzie istniał tylko podczas wykonywania tej jednej instrukcji. } // . } Można tę funkcję wywołać tak: Zespolona z(3. to kompilator wygeneruje go automatycznie. double modul(). W klasie można (i zwykle tak się robi) zdefiniować wiele konstruktorów.. // OK. to musielibyśmy zrobić to następująco: class Zespolona{ private: // tu można pominąć private: double re. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 92 / 201 • jest wywoływany niejawnie przy przekazywaniu parametrów do funkcji i przy przekazywaniu wyników funkcji!!! Klasa Zespolona z konstruktorem Teraz deklaracja naszej klasy wygląda następująco: class Zespolona{ private: // tu można pominąć private: double re. Można go wywołać jedynie przy tworzeniu nowego obiektu danej klasy. PO i C++ im J.4). • jeśli żadnego takiego konstruktora nie zdefiniujemy.modul().. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 90 / 201 Konstruktory Na szczęście w C++ możemy temu skutecznie zaradzić. jeśli chcemy mieć tablice obiektów tej klasy. double). // Błąd składniowy (choć wzór poprawny) { Zespolona z1. przekazywanym przez referencję. public: // konstruktory Zespolona(double. Nie można podać typu wyniku konstruktora.re+z. która będzie inicjować liczbę. // operacje Zespolona dodaj(Zespolona). ale . • jeśli składowa będąca klasą lub strukturą nie ma konstruktora bezargumetowego bądź jest on niedostępny. policz cos(z). }. cout << z1. to można wywołać tę funkcję także tak: policz cos( Zespolona(3. Rozwiązaniem są konstruktory. } Konstruktor kopiujący w klasie Zespolona Dla klasy Zespolona nie ma potrzeby definiowania konstruktora kopiującego (ten wygenerowany automatycznie przez kompilator zupełnie nam w tym przypadku wystarczy).

public: // konstruktory Zespolona(double.. tyle że poprzedzona tyldą. public: int skl3. • Zalety dziedziczenia: • Jawne wyrażanie zależności między klasami (pojęciami). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 100 / 201 Destruktor w klasie Zespolona W klasie Zespolona destruktor nie jest potrzebny. Oznacza to. oznacza że składowe klasy po nim wymienione są widoczne w podklasach (bezpośrednich i dalszych). double i){ re = r. występujące w przykładzie. Destruktor jest odpowiedzialny za dwie rzeczy: • zwolnienie pamięci zajmowanej przez obiekt (to sprawa kompilatora). nie są natomiast widoczne z zewnątrz1 . które obiekt sam sobie przydzielił w czasie swego istnienia. to wygodniej by było mieć konstruktor... 201 J. • Programy wymagają stałego dostosowywania do zmieniających się wymagań użytkownika. 1 Dokładna semantyka protected jest nieco bardziej skomplikowana. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 104 / 201 . które obiekty przydzielają sobie najczęściej są fragmenty pamięci. Np. Nie dotyczy to jednak zasobów. Zasobami. J. im = 0. Destruktor to metoda klasy. które są tak na prawdę liczbami rzeczywistymi. • Dziedziczenie umożliwia pogodzenie dwóch sprzecznych dążeń: • Raz napisany. Nie można specyfikować typu wyniku destruktora. który sam dopisuje zero: class Zespolona{ // . Nazwa destruktora jest taka sama jak nazwa klasy. gdy chcemy opisać bardziej wyspecjalizowane obiekty klasy bazowej. public: // konstruktory i destruktory Zespolona(double. ale w praktyce nie ma to znaczenia. class B: public A{ private: int skl4. Gdybyśmy mieli często używać takich liczb.. Zespolona odejmij(Zespolona). Hierarchie klas pozwalają tworzyć hierarchie pojęć. Poniższe wywołanie jest teraz poprawne: policz cos( 6 ). Zespolona::Zespolona(double r. Gdy obiekt kończy swoje istnienie automatycznie zwalnia się zajmowana przez niego pamięć. • zwolnienie zasobów (to nasze zadanie. wyrażając w ten sposób zależności między pojęciami. Zespolona odejmij(Zespolona). Zespolona::Zespolona(double r) { re = r. // operacje Zespolona dodaj(Zespolona). } // . zamiast tworzyć dwa opisy różnych klas. • Klasa pochodna (podklasa) dziedziczy po klasie bazowej (nadklasie). double = 0). zwalnianie zasobów zapisujemy Zdefiniowanie konstruktora liczb zespolonych z jednym argumentem (liczbą typu double) ma dalsze konsekwencje. }.0). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 103 / 201 J. by chcieć używać liczb zespolonych. Ułatwianie sobie życia cd Przedstawione rozwiązanie jest zupełnie poprawne. Przykładowa podklasa Słowo protected. double modul(). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 97 / 201 26 marca 2011 98 / 201 Ułatwianie sobie życia cd Uwaga: nie można deklarować Zwalnianie zasobów wartości domyślnej i w nagłówku funkcji i w jej implementacji. Można definiować wiele konstruktorów. możemy jawnie zapisać. // operacje Zespolona dodaj(Zespolona).. protected: int skl2. Możemy to jednak zapisać prościej korzystając z parametrów domyślnych: class Zespolona{ private: // tu można pominąć private: double re. PO i C++ im J. kompilator C++ na podstawie listy argumentów zdecyduje. J. protected: int skl5. Jak definiujemy podklasy Przykładowa klasa bazowa: class A{ private: int skl1. • Dziedziczenie umożliwia tworzenie hierarchii klas. • Unikanie ponownego pisania tych samych fragmentów programu (ang. public: // konstruktory Zespolona(double). public: int skl6.. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 101 / 201 Wprowadzenie • Dziedziczenie jest jednym z najistotniejszych elementów obiektowości.Ułatwianie sobie życia Jest zupełnie naturalne. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 99 / 201 J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 102 / reuse). }. Klasę pochodną tworzymy wówczas. Destruktor nie ma parametrów. Zespolona(Zespolona&). Zespolona(Zespolona&). sprzętowych itp. ale można go zdefiniować: class Zespolona{ private: // tu można pominąć private: double re. uruchomiony i przetestowany program powinien zostać w niezmienionej postaci. double modul(). // . Nie można w nim używać instrukcji return z parametrem.. }. }. }. ˜Zespolona(). Rozwiązaniem tego problemu są destruktory. że każdy obiekt klasy pochodnej jest obiektem klasy bazowej. jako treść destruktora). • Klasy odpowiadają pojęciom występującym w świecie modelowanym przez program. im.. O tym jak definiować konwersje w drugą stronę powiemy później (omawiając operatory). że każdy kwadrat jest prostokątem. double = 0).. } J. którego należy użyć. im. void m(). Możemy to teraz robić następująco: Zespolona z(8. oznacza zdefiniowanie konwersji z typu T do typu K. Klasa może mieć co najwyżej jeden destruktor. którego można wywołać z jednym parametrem typu T. Innymi słowy zdefiniowanie w klasie K konstruktora. Jabłonowski (MIM UW) = i. Zespolona::˜Zespolona(){ // W tej klasie nie mamy żadnych zasobów do zwolnienia } J.

// OK fAwsk(&b). // Błąd. nie ma takiej składowej (to samo dla skl5 i skl6) i = b. A ma za mało składowych fB(b).skl4. • składowe chronione (protected) są widoczne w klasie. // OK fAwsk(&a). void m(). ∗aw. // Składowa klasy C } void D::m(){ a = 2. // Błąd. // OK! skl3 = 3. i w funkcjach/klasach z nią zaprzyjaźnionych. A::A(&A) automatyczny konstruktor zadziała fAref(a). tak samo jak b = a.skl4. // Błąd. B b. // Składowa klasy D a. z której pochodzą. ar = br. // OK. // OK } int main(){ A a.Przykłady użycia void B::m(){ skl1 = 1. Przesłanianie nazw cd void C::m(){ a = 1. składowa niewidoczna i = a. // Błąd. A::A(&A) fA(b). D b. // Błąd.m(). składowa niewidoczna i = b. Niestety. A ma za mało składowych fBref(b). PO i C++ 26 marca 2011 i = b. // OK fBwsk(&a). a = b. • Ma on postać ::. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 111 / 201 J. // Składowa klasy C b. // Błąd.skl3. // OK! i = b. składowa niewidoczna i = a. z której pochodzą.skl6. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 108 / 201 Operator zasięgu Dostęp do zmiennej globalnej za pomocą operatora zasięgu • Do odwoływania się do składowych z nadklas służy operator zasięgu. // OK fAref(b). a. }. // OK br = ar.skl3. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 110 / 201 Zgodność typów Obiekt klasy pochodnej jest obiektem klasy bazowej. składowa niewidoczna i = Jabłonowski// Błąd. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 106 / 201 Przesłanianie nazw W podklasach można deklarować składowe o takiej samej nazwie jak w nadklasach: class C { public: int a. mimo że nie mają bezpośredniego dostępu do prywatnych odziedziczonych składowych. // OK.skl1. }. // Błąd. } int i. // Parametr ::i = 5. // OK fBref(a).a = 2. co by miało znaczyć bw−>skl6 ? Zgodność typów parametrów fA(a). // OK i = a. i = a. void m(). // Błąd.skl5. A ma za mało składowych fBwsk(&b).a = 2. co miałoby być wartościami // zmiennych obiektowych występujących w B a w A nie? // Byłoby poprawne po zdefiniowaniu: // B& B::operator=(A&). // Błąd. A::operator= b = a. // Składowa klasy D } int main (){ C a. void m(int i){ i = 3. Uwaga: obiekty podklas. składowa niewidoczna i = b. • składowe publiczne (public) są widoczne wszędzie tam. mają także i te odziedziczone składowe. (MIM UW) J. gdzie jest widoczna sama klasa.skl1.skl2. int i. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 107 / 201 J. &ar=a. // Zmienna globalna } J. // OK J. // OK. // OK fB(a).m(). // Błąd. // Błąd. // OK } Podsumowanie • składowe prywatne (private) są widoczne jedynie w klasie. // Składowa klasy D } To samo dotyczy metod.skl2. składowa niewidoczna skl2 = 2. składowa niewidoczna b. &br=b. gdzie można używać obiektów z klasy bazowej. // Błąd. B b. 105 / 201 J. J. nie zawsze to jest możliwe (i nie zawsze ma sens): A a. // Składowa klasy C b. // OK bw = aw. chcielibyśmy więc. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 109 / 201 J. class D: public C { public: int a. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 112 / 201 . żeby można było wykorzystywać go wszędzie tam. // Błąd. aw = bw. i w funkcjach/klasach z nią zaprzyjaźnionych oraz w jej klasach pochodnych i funkcjach/klasach z nimi zaprzyjaźnionych. // OK skl4 = skl5 = skl6 = 4. ∗bw. • Przykład użycia: void D::m(){ a = C::a.

Jest to niezgodne ze składnią C++ (taki program się nie skompiluje). ale zwiększa czytelność przykładów. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 114 / 201 Przykłady ilustrujące rodzaje dziedziczenie Oto przykłady ilustrujące przedstawione reguły: class A{ public: int i. Przedstawione tu mechanizmy określania dostępu do klasy podstawowej mają zdecydowanie mniejsze znaczenie. }. // Błąd pb3−>a = 1. zaś jeśli nazwa klasy bazowej jest poprzedzona słowem private. funkcje/klasy zaprzyjaźnione z nią oraz jej klasy pochodne i funkcje/klasy zaprzyjaźnione z nimi. // Błąd } J. że klasa pochodna dziedziczy po klasie bazowej. // Błąd (C2::f nie wie. J. // . // OK pa = pb2. niż omówione poprzednio mechanizmy ochrony dostępu do składowych. void pokaż(). to jej składowe publiczne i chronione zachowują się w klasie pochodnej jak prywatne. B3∗ pb3){ A∗ pa = pb1. Określa to słowo wpisane w deklaracji podklasy przed nazwą klasy bazowej. Zatem jeśli nazwa klasy bazowej jest poprzedzona słowem protected. B3∗ pb3){ A∗ pa = pb1. B2∗. C ∗cwsk=&d. B2∗ pb2. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 119 / 201 J. Powrócimy do tego tematu przy okazji funkcji wirtualnych. • private: wie tylko klasa pochodna i funkcje/klasy z nią zaprzyjaźnione. B2∗ pb2. int). // OK pb2−>a = 1. class B1: public A{}. Decyduje ono o tym kto wie. void przesuń(int. }. cwsk−>m(). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 117 / 201 J. // Błąd (f nie wie. // OK pb1−>a = 1. // Błąd (B3::f nie wie. class B2: protected A{}.Na co wskazują wskaźniki? Dziedziczenie public. B2∗ pb2. class B3: private A{ void m(B1∗. B3∗ pb3){ A∗ pa = pb1. 2 W tym i w pozostałych przykładach pozwalamy sobie zapisywać identyfikatory z polskimi znakami. B3∗).. że B2 jest // podklasą A pb2−>a = 1. }. to składowe publiczne tej klasy zachowują się w klasie pochodnej jak chronione. J. • pozwala dostawać się (zgodnie z omówionymi poprzednio regułami dostępu) do składowych klasy bazowej. // OK pb3−>a = 1. void ustaw(int. jaka funkcja powinna się wywołać? cwsk pokazuje na obiekt klasy D. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 118 / 201 Podsumowanie Przykład klasy Figura Rozważmy poniższy (uproszczony) przykład:2 class Figura{ // . void schowaj(). to domyślnie zostanie przyjęte private (dla struktur public). int). że B3 jest podklasą A pb3−>a = 1. Co daje ta wiedza? Dwie rzeczy: • pozwala dokonywać niejawnych konwersji ze wskaźników do podklas na wskaźniki do nadklas. // Błąd } void B3::m(B1∗ pb1. // OK pb1−>a = 1. // OK } J. // OK pa = pb2. Ale kompilator o tym nie wie i wygeneruje kod wywołujący funkcję z klasy C.. Odwołania z funkcji void m(B1∗ pb1. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 120 / 201 . Jabłonowski (MIM UW) PO i C++ 26 marca 2011 115 / 201 J. protected: int x. Klasa może dziedziczyć po nadklasie na trzy różne sposoby.. że B2 jest podklasą A) pb2−>a = 1.y. Jeśli pominiemy to słowo. // OK pa = pb2. B3∗).. B2∗. }. Tym słowem może być: • public: wszyscy wiedzą • protected: wie tylko klasa pochodna. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 113 / 201 J. // położenie na ekranie public: Figura(int. protected i private Zwróćmy uwagę na interesującą konsekwencję reguł zgodności przedstawionych powyżej: D d. int). class C2: public B2{ void m(B1∗. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 116 / 201 Odwołania z podklasy klasy dziedziczącej w sposób chroniony Odwołania z klasy dziedziczącej prywatnie void C2::m(B1∗ pb1. // OK pb1−>a = 1. // OK pa = pb3. // Błąd pa = pb3. zaś doprowadzenie do zgodności ze składnią C++ jest czysto mechaniczne i nie powinno Czytelnikowi sprawić kłopotu. // Błąd pa = pb3.

• Składnia deklaracji metod wirtualnych. C c. y = n y.y).y). int. a = c. schowaj(). bo a jest obiektem klasy A } Implementacja: • Jest efektywna. p−>m(3). tablica metod wirtualnych. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 122 / 201 Przykład klasy Figura cd Metody wirtualne • Czemu nie działa przykład z Okręgiem? Okrąg o(20. }. // A::m() p = new C. // A::m() p = new B. 200). A a. // Nie przesuwa ! • Czy instrukcja warunkowa lub wyboru jest tu rozwiązaniem? • Różnica między metodami wirtualnymi a funkcjami.. int n y){ ustaw(x. Okrąg::Okrąg(int x. Jabłonowski (MIM UW) // j. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 125 / 201 J. class C: public B{ public: void m(int). // A::m().w. n y).pokaż(). pokaż(). // C::m(). int main(){ A ∗p. bo ∗p jest obiektem klasy C J. • nie definiować. } Figura::przesuń(int n x. J. 10).przesuń(100. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 124 / 201 Przykład z trójpoziomową hierarchią klas cd Przykład z trójpoziomową hierarchią klas cd class A{ public: void virtual m(int). int r): Figura(x.Przykład klasy Figura cd Figura::Figura(int n x. int). int n y){ schowaj(). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 128 / 201 . } PO i C++ 26 marca 2011 121 / 201 Przykład klasy Figura cd class Okrąg: public Figura{ protected: int promień. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 127 / 201 J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 123 / 201 J. public: Okrag(int. } Figura::pokaż(){ // Nie umiemy narysować dowolnej figury } Figura::schowaj(){ J. // }. • Np. } Figura::ustaw(int n x. class B: public A{ }. // Rysuje okrąg o.m(3). p−>m(3). ustaw(n x. promień(r){} J. pokaż(). int n y){ x = n x. p = new A. p−>m(3). • W podklasie klasy z metoda wirtualną można tę metodę: • zdefiniować na nowo (zachowując sygnaturę). // Ta metoda jest wirtualna! }. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 126 / 201 Przykład z trójpoziomową hierarchią klas cd Implementacja metod wirtualnych // Ale: // .. a. // Okrąg o zadanym położeniu i promieniu o. 30. J. int y.

().. • następnie inicjuje się składowe (w kolejności deklaracji. definiować ich znaczenie w sposób odpowiedni dla własnych klas. • Poniższe operatory można przeciążać: • • • • • • • • To drugie wymaga. public!: B(A&). . zawsze może to też być funkcja) o nazwie składającej się ze słowa operator i nazwy operatora (np. • Klasy definiowane przez użytkownika muszą być co najmniej tak samo • Przeciążanie operatora polega na zdefiniowaniu metody (prawie dobrymi typami jak typy wbudowane. powinniśmy więc napisać: class Figura{ // . %. >>=. że tak rzeczywiście będzie. ˜. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 130 / 201 J. <. A(). >>. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 135 / 201 J. &&. • destruktory (niestatycznych) obiektów składowych. tzn. my nie musimy już (i nie możemy) inicjować dalszych nadklas oraz składowych z nadklas. Na szczęście w podklasie musimy zadbać o inicjowanie jedynie: • bezpośredniej nadklasy. virtual void schowaj() = 0. Ponadto klasy mogą mieć składowe również będące obiektami klas . Możemy łatwo zagwarantować. <=. −>. Kolejność inicjacji: • najpierw inicjuje się klasę bazową. . klasa pochodna też będzie abstrakcyjna. • destruktory klas bazowych. &=. +. My wywołując go (w celu zainicjowania bezpośredniej klasy bazowej) spowodujemy (pośrednio) inicjację wszystkich warstw pochodzących z dalszych klas bazowych. Ważne uwagi: • W korzeniu hierarchii klas musi być destruktor wirtualny. +=. Nie musimy inicjować nadklasy. każda odpowiadająca jednej z nadklas w hierarchii dziedziczenia. by twórca klasy mógł definiować operatory.Klasy abstrakcyjne Często tworząc hierarchię klas na jej szczycie umieszcza się jedną (lub więcej) klas.je też trzeba zainicjować w konstruktorze. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 129 / 201 Inicjacja w hierarchiach klas Inicjacja składowych Czemu ważna jest możliwość inicjowania składowych: Składnia inicjacji: po nagłówku konstruktora umieszczamy nazwę nadklasy (składowej). delete.. −>∗. ∗=. −=. !=. O ile w klasie pochodnej nie przedefiniujemy wszystkich takich funkcji.) J. kolejności inicjatorów). >. ˆ. Tworząc taki obiekt musimy zadbać o zainicjowanie wszystkich warstw. deklarując jedną (lub więcej) metod w tej klasie jako czyste funkcje wirtualne. class B{ A a. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 134 / 201 Motywacja Opis • Większość operatorów języka C++ można przeciążać. W podanym poprzednio przykładzie z figurami. składa się z wielu warstw. że podobiekty i składowe zostaną zniszczone w odwrotnej kolejności niż były inicjowane. ∗/ public: A(int). Oznacza to. Konstruktory i destruktory w hierarchiach klas Jak pamiętamy obiekt klasy dziedziczącej po innej klasie przypomina kanapkę. • Uwaga na metody wirtualne w konstruktorach i destruktorach. !. %=.. }. ∗. <<. tzn. Składniowo oznacza to tyle. virtual void pokaż() = 0. że po ich nagłówku (ale jeszcze przed średnikiem) umieszcza się =0 i oczywiście nie podaje się ich implementacji. operator=). ++. =. /. /=. ==. • muszą dać się wygodnie używać. >=. i B::B(A& a2): a(a2){}. jeśli ta posiada konstruktor domyślny (i wystarczy nam taka inicjacja). Nie musimy inicjować składowych. J. <<=. • Definiowanie operatorów wymaga szczególnej ostrożności. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 136 / 201 . że nie będziemy tworzyć obiektów tych klas. o których wiemy. |. new. &. ||. niezależnie od class A{ /∗ . −−. a po niej w nawiasach parametr(y) konstruktora. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 132 / 201 Inicjacja składowych cd Niszczenie obiektu Kolejność wywoływania destruktorów: Rozważmy następujące wersje konstruktora dla B: B::B(A& a2){ a = a2. [].. }. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 133 / 201 J. |=. • własnych składowych. J. } • destruktor w klasie. (Uniezależnienie od kolejności inicjatorów służy zagwarantowaniu tego. }. Powód jest oczywisty: to robi konstruktor nadklasy. tzn. −. że: • muszą dać się efektywnie zaimplementować. ˆ=. J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 131 / 201 J. które mają konstruktor domyślny (i wystarcza nam taka inicjacja).

Jabłonowski (MIM UW) PO i C++ 26 marca 2011 142 / 201 Kiedy definiować operator jako funkcję. class Zespolona{ // public: Zespolona(double). // przyrostkowy a++ }.∗. • Operatory =.jak i dwuargumentową: • +. jak również typ wyniku. J. • Nie ma obowiązku zachowywania równoważności operatorów występujących w przypadku typów podstawowych (np. to musi ona mieć co najmniej jeden argument będący klasą bądź referencją do klasy. • Operatorów ++ oraz −− można używać zarówno w postaci przedrostkowej jak i przyrostkowej. J. • Jeśli zadeklarowano obie postacie. żeby 1+3 zawsze znaczyło 4. . W celu rozróżnienia definicji przedrostkowego i przyrostkowego ++ (−−) wprowadza się dla operatorów przyrostkowych dodatkowy parametr typu int (jego wartością w momencie wywołania będzie liczba 0). • gdy istotne jest równe traktowanie obu argumentów operatora.operator++(0). a++. b).operator+(c)). (). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 141 / 201 J. nast. J. łączności ani Operatory jednoargumentowe • Operator jednoargumentowy (przedrostkowy) @ można zadeklarować jako: • (niestatyczną) metodę składową bez argumentów: liczby argumentów. J. punkt) ustalać ich typy. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 137 / 201 J. a kiedy jako metodę? Operator jako metoda . • Operator przeciążony nie może mieć argumentów domyślnych. int main(){ X a. ∗. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 144 / 201 . // Konstruktor ale i konwersja Zespolona operator+(const Zespolona&). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 139 / 201 skorzystać używa się standardowego mechanizmu dopasowywania argumentów. }. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 138 / 201 Uwagi dotyczące definiowania operatorów • Definiując operator nie można zmieniać jego priorytetu. &. ++a nie musi być tym samym co a+=1). Można natomiast dowolnie (p.operator++(). } Operatory dwuargumentowe • Operator dwuargumentowy @ można zadeklarować jako: • (niestatyczną) metodę składową z jednym argumentem: typ1 operator@(typ2) i wówczas a @ b jest interpretowane jako: a.niesymetryczne traktowanie argumentów • Najlepiej jako metodę. // Uwaga: ze względu na znaczenie tych operatorów // pierwszy z nich raczej definiuje się jako: // X& operator++(). // przedrostkowy ++a X operator++(int). • jak i funkcyjnej (tej postaci praktycznie się nie stosuje): a. [] i −> można deklarować jedynie jako (niestatyczne) metody. a nie np.operator=(b. J. • Jeśli zadeklarowano obie postacie. // to samo co: a. ::. ?:. typ3) i wówczas a @ b jest interpretowane jako: operator@(a. sizeof (ani symboli preprocesora # i ##) • Operatory new i delete mają specyficzne znaczenie i nie odnoszą się do nich przedstawione tu reguły.przykład class X{ public: X operator++(). to do określenia z której z nich operatora przypisania. który jest bardziej złożonym przypadkiem). Powód: chcemy. to do określenia z której z nich skorzystać używa się standardowego mechanizmu dopasowywania argumentów. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 143 / 201 J.. // to samo co: a. -2.Opis cd Opis cd • Dla poniższych operatorów można przeciążać zarówno ich postać • Tak zdefiniowane metody (funkcje) można wywoływać zarówno w jedno. • Tych operatorów nie można przeciążać: • .operator@(b) • funkcję przyjmującą dwa argumenty: typ1 operator@(typ2. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 140 / 201 Operatory jednoargumentowe . −.operator@() • funkcję przyjmującą jeden argument: typ1 operator@(typ2) i wówczas @a jest interpretowane jako: operator@(a). • Metody operatorów dziedziczą się (poza wyjątkiem kopiującego typ operator@() i wówczas @a jest interpretowane jako: a. ++a. • Jeśli definiujemy operator jako funkcję. notacji operatorowej: a = b + c. • Nie zawsze można: • gdy operator dotyczy dwa klas.

Jabłonowski (MIM UW) w czasie kompilacji otrzymamy komunikaty o próbie użycia PO i C++ 26 marca 2011 146 / 201 tego operatora poza tą klasą).operator()(arg1.. z1 = z2 + 1. • zdefiniować go (jako pusty) w części private (to jest dobre rozwiązanie. Język C++ nie definiuje kolejności tych przypisań. • Automatycznie definiowany kopiujący operator przypisania w podklasie wywołuje operator przypisania z nadklasy. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 148 / 201 Operator dostępu do składowej klasy Wyrażenie: wyrażenie proste −> wyrażenie proste Konwersje typów uważa się za operator jednoargumentowy. J. gdy są jednoznaczne. Musimy zabronić jego stosowania. obiekt klasy albo referencję do klasy. że dla definiowanej klasy operator= nie ma sensu. Można to zrobić na dwa sposoby: • zdefiniować jego treść jako wypisanie komunikatu i przerwanie działają J.operator[](y) interpretuje się jako: x. // Niepoprawne. W dwu ostatnich przypadkach. jako przypisanie składowa po składowej (więc nie musi to być przypisywanie bajt po bajcie). arg3) interpretuje się jako operator dwuargumentowy. }. dla nich operator= też musi działać poprawnie! • Jeśli uważamy. jeśli samemu definiujemy ten operator. arg2. J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 149 / 201 J. Wyrażenie: x −> m • W C++ możemy specyfikować konwersje typów na dwa sposoby: • do definiowanej klasy z innego typu (konstruktory). gdzie X jest nazwą klasy. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 147 / 201 J. nie było by tego problemu.. zdefiniowany przez kompilator. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 150 / 201 Konwersje typów Operatory konwersji class X { /∗ . bo musi być dokładnie taki sam jak w nazwie operatora. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 152 / 201 . z2. co jest bardzo nienaturalne. ∗/ Y(X).niesymetryczne traktowanie argumentów Kopiujący operator przypisania • Kopiujący operator przypisania jest czymś innym niż konstruktor kopiujący! • O ile nie zostanie zdefiniowany przez użytkownika. ta klasa musi mieć zdefiniowany operator −> (w końcu musimy uzyskać coś co będzie wskaźnikiem). }.Operator jako metoda . Zatem operator−>() musi dawać wskaźnik do klasy. arg2. • z definiowanej klasy do innego typu (operatory konwersji). Wyrażenie: wyrażenie proste [ wyrażenie ] uważa się za operator dwuargumentowy z wyrażeniem prostym jako pierwszym argumentem i..operator−>())−>m • Są one używane niejawnie wraz z konwersjami standardowymi. • Konwersje zdefiniowane przez użytkownika stosuje się jedynie wtedy. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 151 / 201 J.. • Można zdefiniować także inne (niż kopiujący) operatory przypisania. bo teraz już J. • Zwykle typ wyniku definiuje się jako X&. dla której definiujemy operator=. to musimy sami o to zadbać. Zatem wyrażenie: x[y] interpretuje się jako: x. arg3) J. to będzie Przy przedstawionych deklaracjach można napisać: Zespolona z1. • taka metoda musi instrukcją return przekazywać obiekt odpowiedniego typu. być może pustą. bo zadziała dopiero w czasie wykonywania programu). to nie wystarczy go nie definiować (bo zostanie wygenerowany automatycznie). interpretuje się jako: (x. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 145 / 201 Operator wywołania funkcji Wywołanie: wyrażenie proste( lista wyrażeń ) Operator indeksowania programu (kiepskie. • Przy liczeniu jednej wartości kompilator może użyć niejawnie co najwyżej jednej konwersji zdefiniowanej przez użytkownika. • Uwaga na przypisania x = x. class Y { /∗ . Gdybyśmy zdefiniowali operator + jako funkcję. ∗/ X(int). bo Y(X(1)) zawiera już dwie konwersje użytkownika • Są metodami o nazwie: operator nazwa typu • nie deklarujemy typu wyniku. listą wyrażeń jako drugim. Y a = 1. // Niejawne użycie konwersji ale nie można napisać: z1 = 1 + z2. Zatem wywołanie: x(arg1. • Oba te rodzaje konwersji nazywa się konwersjami zdefiniowanymi przez użytkownika.

public: Liczba(int). • W C czy Pascalu nie jest to możliwe. tab = new EltStosu∗[size]. Opis zastosowania tych metod nie mieści się w ramach tego wykładu. int Stos::empty(){ while (!s. int i.empty(). }. } void Stos::push(EltStosu elt){ if (top < size){ tab[top] = new EltStosu(elt). i++) delete tab[i]. J.in. delete tab[top]. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 156 / 201 Stos: klasa Stos // −−−−−−−−−−−− // Klasa Stos // −−−−−−−−−−−− class Stos{ // Możliwie najprostsza implementacja stosu private: // Nie przewiduje dziedziczenia EltStosu∗∗ tab. // Tablica wskaźników do elementów stosu unsigned size. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 157 / 201 Stos: implementacja klasy Stos // −−−−−−−−−−−−−−−−−−−−−−−−−− // Implementacja klasy Stos // −−−−−−−−−−−−−−−−−−−−−−−−−− Stos::Stos(unsigned s){ size = s. będą używane przez kompilator w momencie wywoływania operacji new i delete do (odpowiednio) przydzielania i zwalniania pamięci. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 155 / 201 J. i<top. dwukrotne kopiowanie elementu stosu EltStosu res(∗tab[−−top]). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 153 / 201 J. J.push(Liczba(3)). } cout << ”\n:” << i. void push(EltStosu). EltStosu pop(). i = s. }. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 160 / 201 . J.pop(). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 154 / 201 Czemu potrzeba w C++ szablonów Stos: klasa EltStosu • Chcemy mieć ATD. return res. // Nie przejdzie bez jawnej konwersji } s. Liczba::Liczba(int k): i(k) {} // Ze względu na konieczność usuwania wykonuje się tu int main(){ //Stos s. } PO i C++ 26 marca 2011 158 / 201 Stos: klasa EltStosu Stos: klasa EltStosu EltStosu Stos::pop(){ A oto przykładowy program główny: if (!top) blad(”brak elementów na stosie”). • Dziedziczenie zdaje się oferować rozwiązanie. // Rozmiar tablicy ∗tab unsigned top. delete[] tab. } Stos::˜Stos(){ for (unsigned i=0. Muszę zdefiniować podklasę reprezentującą wkładane elementy: class Liczba: public EltStosu{ private: int i. // Indeks pierwszego wolnego miejsca na stosie public: Stos(unsigned = 1024). J.push(Liczba(5)). int empty(). s. } J. cout << ”\nStan stosu: ” << s. top = 0. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 159 / 201 J. class EltStosu{ // Podklasy tej klasy będą elementami stosu. • Przykład . Zostaną omówione wraz ze strumieniami. top++. Operatory << i >> służą (m.empty()){ return top == 0. Jabłonowski (MIM UW) } else blad(”przepełnienie stosu”).stos elementów dowolnego typu. }.Operatory new i delete Operatory << i >> Jeśli zostaną zdefiniowane. virtual ˜EltStosu(){}.) do wczytywania i wypisywania obiektów definiowanej klasy. ˜Stos(). } return 0.

• Zalety: • można naraz trzymać na stosie elementy różnych typów.Błędy w przedstawionym rozwiązaniu Błędy w przedstawionym rozwiązaniu cd • Uniknięcie kopiowania w implementacji push wymaga zmian także w klasach EltStosu i Liczba na class EltStosu{ public: virtual EltStosu∗ kopia() = 0. } else blad(”przepełnienie stosu”). wymaga to też zmian w treści: EltStosu∗ Stos::pop(){ if (!top) blad(”brak elementów na stosie”). } J. while (!s. virtual ˜EltStosu(){}. top++. return tab[−−top].jest to wprawdzie poprawne językowo. virtual ˜EltStosu(){}. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 166 / 201 Błędy w przedstawionym rozwiązaniu . dającą wskaźnik do kopii oryginalnego obiektu: class EltStosu{ public: virtual EltStosu∗ kopia() = 0. • muszę w niej zdefiniować konstruktor i operację kopia. Trzeba jeszcze tę metodę przedefiniować w klasie Liczba: EltStosu∗ Liczba::kopia(){ return new Liczba(i). Żeby temu zapobiec definiujemy w klasie EltStosu metodę czysto wirtualną kopia. int main(){ Stos s. s.eliminujemy przekazywanie przez wartość Błędy w przedstawionym rozwiązaniu . Zmieńmy więc typ jej wyniku na wskaźnik do EltStosu.push(Liczba(5)). wkładanego obiektu).kopia(). byleby były podtypami EltStosu.pop())−>i. } else blad(”przepełnienie stosu”). bo w niej wywołujemy konstruktor kopiujący z klasy EltStosu (który skopiuje tylko tę nieciekawą część • Operacja push ma argument przekazywany przez wartość . bo pochodzących z klasy EltStosu. • muszę zdefiniować podklasę EltStosu.zmieniona treść push Błędy w przedstawionym rozwiązaniu . J. cout << ”\nStan stosu: ” << s. } J. }.kopia().empty()){ i = ((Liczba∗)s. • użytkownik musi dokonywać rzutowań typu przy pobieraniu (a to jest nie tylko niewygodne.push(Liczba(3)). wiedzieć co się zdjęło). top++. } void Stos::push(EltStosu& elt){ if (top < size){ tab[top] = elt. • Poważne wady: • użytkownik musi pamiętać o usuwaniu pamięci po pobranych obiektach.zły typ wyniku pop • Treść push jest teraz taka: void Stos::push(EltStosu& elt){ if (top < size){ tab[top] = elt. Jabłonowski (MIM UW) PO i C++ } Niestety tak zapisany program zawiera sporo błędów: • Nagłówek push wymaga zmiany na void Stos::push(EltStosu& elt) J. więc skopiuje się tylko ten nieciekawy fragment. } • Metoda pop też jest zła: wynik jest typu EltStosu. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 164 / 201 Błędy w przedstawionym rozwiązaniu . Jabłonowski (MIM UW) PO i C++ 26 marca 2011 168 / 201 . ale spowoduje że na stosie będą kopie jedynie fragmentów obiektów Liczba i w dodatku tych nieciekawych fragmentów. } } Błędy w przedstawionym rozwiązaniu podsumowanie Podsumujmy wady tego rozwiązania: • Wady: • wkładając muszę używać konwersji to co chcę włożyć −> podklasa EltStosu.empty(). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 167 / 201 J.zmiana treści programu • Musimy jeszcze zmienić treść programu głównego. Trzeba tylko umieć potem je z tego stosu zdjąć (tzn. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 161 / 201 26 marca 2011 162 / 201 Błędy w przedstawionym rozwiązaniu . Jabłonowski (MIM UW) PO i C++ 26 marca 2011 165 / 201 J. Można temu prosto zaradzić deklarując nagłówek push następująco: void Stos::push(EltStosu& elt) • Zyskujemy przy okazji jedno kopiowanie mniej. J. cout << ”\n:” << i. ale przede wszystkim niebezpieczne). }. int i. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 163 / 201 J. EltStosu∗ Liczba::kopia(){ return new Liczba(i).eliminujemy konstruktor kopiujący • Nadal operacja push jest zła. // Nie przejdzie bez jawnej konwersji s.

push(3).. int t1[100]. • Może też być szablonem. tab[top] = (MIM UW) top++. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 170 / 201 Przykład definicji metod dla szablonu klasy Deklarując metody dla tego szablonu musimy je poprzedzać informacją. } Przykład definicji metod dla szablonu klasy template <class T> T Stos<T>::pop(){ if (!top) blad(”brak elementów na stosie”). sortuj(t1. int empty().empty(). } else blad(”przepełnienie stosu”). // Indeks pierwszego wolnego miejsca na stosie public: Stos(unsigned = 10). } template <class T> Stos<T>::˜Stos(){ for (unsigned i=0. J. int i> class A {}. cout << ”\nStan stosu: ” << s. void push(T&). delete[] tab. Zespolone t2[20]. // wywołanie z T równym int sortuj(t2.Deklaracje szablonów Przykład deklaracji szablonu Oto przykład deklaracji szablonu: template <class T> class Stos{ // Możliwie najprostsza implementacja stosu protected: T∗∗ tab. 20). } PO i C++ 26 marca 2011 171 / 201 J. generuje je kompilator. return res. • Parametr szablonu może także być zwykłym parametrem typu całkowitego. • Szablon klasy . // Rozmiar tablicy ∗tab unsigned top. int i. że są szablonami metod: template <class T> Stos<T>::Stos(unsigned s){ size = s. } template <class T> void Stos<T>::push(T& elt){ if (top < size){ J. while (!s. // Ze względu na konieczność usuwania są delete tab[top]. • Można użyć szablonu jako klasy bazowej: class Stos specjalny: public Stos<int> { /∗ . } template <class T> int Stos<T>::empty(){ return top == 0. • Oto deklaracja uniwersalnej funkcji sortującej: template<class T> void sortuj(T tab[]. ∗/ } (podklasa może być zwykłą klasą bądź znów szablonem). wartości proste) J.empty()){ i = s. ˜Stos(). } } Używanie szablonów . }. • Oprócz szablonów klas można także tworzyć szablony funkcji. template<template<class T> class U> class A { }. i++) delete tab[i]. class) • Parametry (typy. cout << ”\n:” << i. tab = new T∗[size].. i<top. unsigned n) { /∗ Treść tej funkcji ∗/ } • Oto przykład: // . Jabłonowski (MIM UW) PO i C++ 26 marca 2011 169 / 201 J. J.. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 172 / 201 Używanie szablonów • Składnia użycia szablonu klasy • Przykład int main(){ Stos<int> s.. wyliczeniowego lub wskaźnikowego template<class T. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 175 / 201 J.matryca klas • Składnia (template.push(5). // Tablica wskaźników do elementów stosu unsigned size. // wywołanie z T równym Zespolona • Każdy argument szablonu funkcji musi wystąpić jako typ argumentu w szablonie funkcji.pop(). s. s. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 174 / 201 Szablony funkcji Szablony funkcji • Przy wywoływaniu funkcji zdefiniowanej przez szablon nie podaje się jawnie argumentów szablonu. typename. T pop(). T res(∗tab[−−top]). // dwie operacje kopiowania w pop(). 100). Jabłonowski new T(elt).dalsze przykłady • Można zadeklarować nowy typ będący ukonkretnionym szablonem: typedef Stos<int> StosInt. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 176 / 201 . Jabłonowski (MIM UW) PO i C++ 26 marca 2011 173 / 201 J. top = 0.

• Funkcja zgłasza wyjątek. // . J.. • Jako trzeci parametr można podać funkcję porównującą elementy.sort • W bibliotece standardowej jest uniwersalna funkcja sortująca sort. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 180 / 201 Idea obsługi wyjątków w C++ Wyjątki . int rozm.. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 178 / 201 Sortowanie tablicy wskaźników do liczb Wprowadzenie do obsługi wyjątków const int n = 10. • Pełne jej omówienie wymaga znajomości STLa (nasze końcowe Sortowanie tablicy liczb zadanej wskaźnikami wykłady). // Treść w wykładzie sort(tab.. • parametr .być może w innym bloku . pokaż(tab. • Szukanie obsługi wyjątku z ”paleniem mostów”. try{ <instrukcje> } catch (<parametr 1>){ <obsługa wyjątku 1> } // .. tab+n). Sposoby reagowania na błędne sytuacje: • komunikat i przerwanie działania programu. • globalna zmienna z kodem błędu..przykład Przykład: class Wektor{ int ∗p.funkcja obsługi błędów. • kod błędu jako wynik. // Treść w wykładzie pokaż wsk(tab. // Treść w wykładzie sort(tab. int∗ tab[n]. • Nie ma mechanizmu powrotu z obsługi wyjątku do miejsca jego zgłoszenia. // Treść w wykładzie pokaż(tab. const int n = 10. • Oczekiwany czas to O(n ∗ lgn).. por wsk). tab+n). } int& Wektor::operator[](int i){ if J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 181 / 201 J. pesymistyczny nie gorszy niż O(n2 ). Jabłonowski (MIM UW) (0<=i && i < rozm) return p[i]. catch (<parametr n>){ <obsługa wyjątku n> } Zgłoszenie wyjątku: throw <wyrażenie>.semantyka Wyjątki . Jabłonowski (MIM UW) PO i C++ 26 marca 2011 183 / 201 PO i C++ 26 marca 2011 184 / 201 . ini wsk(tab..Szablony funkcji .. użyty będzie operator <. • Wyjątek rozumiany jako błąd. tab+n). int tab[n]. // Wyjątek: zły rozmiar wektora Wektor(int r). • Uproszczona wersja opisu: argumenty są parą wskaźników. tab+n). J. // . int& operator[](int i).. pokaż wsk(tab.składnia Składnia instrukcji związanych z obsługą wyjątków: • Celem jest przezwyciężenie problemów z wcześniejszych rozwiązań.. wpp. • Uwaga: zakres sortowany nie obejmuje elementu zadanego drugim wskaźnikiem.. Wektor::Wektor(int r){ if (r<=0) throw Rozmiar(). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 177 / 201 J. }. // . • Kod obsługi wyjątku może być w zupełnie innym miejscu programu. tab+n). // .i wykonana • brak pasującej klauzuli J. tab+n... tab+n). J. // .. bool por wsk(int∗ p1. ini(tab. • To sortowanie nie jest stabilne (ale jest też stable sort). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 179 / 201 J. zależnie od implementacji. else • Zgłoszenie wyjątku rozpoczyna wyszukiwanie obsługi • Klauzule catch są przeglądane w kolejności ich deklaracji • Trzy możliwości • instrukcje nie zgłosiły wyjątku • klauzula wyjątku znaleziona . tab+n). // Wyjątek: wyjście poza zakres class Rozmiar{}. public: class Zakres{}. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 182 / 201 Wyjątki . // Nie <= !!! } // . int∗ p2){ return ∗p1 < ∗p2.

// . } catch (Wektor::Rozmiar) { /∗ . Jabłonowski (MIM UW) PO i C++ 26 marca 2011 188 / 201 Przekazywanie informacji z wyjątkiem .. • Wyjątek jest uważany za obsłużony z momentem wejścia do klauzuli • Wyjątek jest obiektem. public: class Zakres{ public: int indeks. }.. • Pozwala to specjalizować obsługę wyjątków.. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 185 / 201 J. ∗/ } catch (Nadmiar) { /∗ Obsługa nadmiaru ∗/ } catch (BłądMatemat) { /∗ Obsługa pozostałych bł. • Można zagnieżdżać wyjątki. class Niedomiar: public BłądMatemat {}. ∗/ } } void f2(Wektor& w){ try{ /∗ używanie wektora w ∗/ } catch (Wektor::Zakres) { /∗ .. } // .przykład cd void f(){ try{ // używanie wektorów } catch (Wektor::Zakres){ // obsługa błędu przekroczenia zakresu } catch (Wektor::Rozmiar){ // obsługa błędu polegającego na błędnym podaniu rozmiaru } // Sterowanie do chodzi tutaj. // . try{ /∗ używanie w ∗/ } catch (Wektor::Zakres z){ cerr << ”Zły indeks” << z..przykład int& Wektor::operator[](int i){ if (0<=i && i<rozm) return p[i]..przykład Przekazywanie informacji z wyjątkiem . class Nadmiar: public BłądMatemat {}. obsługującej go.. lub // b) zgłoszono wyjątek Zakres lub Rozmiar i obsługa tego // wyjątku nie zawierała instrukcji powodujących wyjście // z funkcji f } Wyjątki . class BłądMatemat {}. J.. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 192 / 201 . Jabłonowski (MIM UW) PO i C++ 26 marca 2011 186 / 201 Wyjątki Przekazywanie informacji wraz z wyjątkiem • Obsługa wyjątku może zgłosić kolejny. mat. ∗/ } J.. try { /∗ . Jabłonowski (MIM UW) PO i C++ 26 marca 2011 187 / 201 J..Wyjątki . • Obiekt może mieć swój stan. ∗/ } } J. class DzielPrzezZero: public BłądMatemat {}.. J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 189 / 201 J. throw Zakres(i).. gdy: // a) nie było zgłoszenia wyjątku.semantyka void f1(){ try{ f2(w). Jabłonowski (MIM UW) PO i C++ 26 marca 2011 190 / 201 Hierarchie wyjątków Hierarchie wyjątków . Zakres(int i): indeks(i) {} }.przykład • Klasy wyjątków można łączyć w hierarchie.... // . Jabłonowski (MIM UW) PO i C++ 26 marca 2011 191 / 201 J. } } class Wektor{ // .. int& operator[] (int i).. void f(Wektor& w){ // .indeks << ”\n”..

Jabłonowski (MIM UW) PO C++ p = new T[t]. które muszą się zawsze wykonać na koniec procedury f. które funkcja zdążyła już sobie przydzielić zanim nastąpił błąd. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 195 / 201 J. try { /∗ coś z plikiem p ∗/ } catch (. // Ponowne zgłoszenie złapanego wyjątku } J.Resource Acquisition Is Initialization) • Bardzo skuteczna technika w C++ • W innych językach: • Java: finilize • C#: instrukcja using J. Rozwiązanie 1 (tradycyjne .. ∗/ } catch (.} operator T∗() {return p. ”w”).a). throw. void używanie pliku(const char∗ np){ Wsk do pliku p(np. • Pomysł: • opakowanie zasobów obiektami • wykorzystanie mechanizmu wyjątków.Dodatkowe własności wyjątków Wyjątki . ”w”). template<class T> Wsk do pamięci::Wsk do pamięci(sizei t r){ J. • Problem .. obudowując zasoby obiektami (pamiętajmy. jest zwalnianie zasobów. X(const char∗x. } J. • Jeśli są wyspecyfikowane ich sprawdzenie odbywa się podczas wykonania programu. ∗/ throw...przykłady • Wznawianie wyjątku throw • Obsługa dowolnego wyjątku void f(){ try { /∗ . ”w”). }. const char ∗ a) {p = fopen(n.niewygodne): void używanie pliku(const char∗ np){ FILE∗ p = fopen(np. // . template<class T> class Wsk do pamięci{ public: T∗ p.. ˜Wsk do pamięci() {delete[] p. } fclose(p). Dzięki wyjątkom możemy bardzo prosto rozwiązać ten problem.zwalnianie zasobów.} }.. że procesowi szukania procedury obsługi błędu towarzyszy usuwanie obiektów lokalnych). } Specyfikowanie wyjątków w interfejsie funkcji • Wyjątki są istotną cechą specyfikacji funkcji. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 196 / 201 Rozwiązanie 2 (eleganckie i ogólne): Rozwiązanie 2 (eleganckie i ogólne): class Wsk do pliku{ FILE∗ p. int y): p(x. • C++ nie wymusza ich specyfikowania. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 200 / 201 . public: Wsk do pliku(const char∗ n..} operator FILE∗() {return p.. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 194 / 201 Zdobywanie zasobów Rozwiązanie 1 (tradycyjne . class Za mało pamięci{}. } ˜Wsk do pliku() {fclose(p). /∗ coś z plikiem p ∗/ } Zdobywanie zasobów jest inicjacją • Zdobywanie zasobów jest inicjacją (RAII .. Wsk do pamięci(size t). • Specyfikowanie braku wyjątków i ich niezgłaszania. pam(r) { /∗ inicjacja ∗/ } // ..niewygodne): Częstym problemem związanym z obsługą wyjątków. Wsk do pamięci<int> pam.){ fclose(p).} // Jeśli będę potrzebował wskaźnika do struktury FILE }. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 197 / 201 J. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 198 / 201 Obsługa wyjątków w konstruktorach class X{ Wsk do pliku p.) { /∗ Instrukcje. 26 marca 2011 199 / 201 J. jeśli nastąpił błąd. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 193 / 201 J.

) { unexpected(). } catch (. } } J.. } catch (x3) { throw. } catch (x2) { throw. x3) { /∗ treść f() ∗/ } Jest to równoważne napisaniu: void f(){ try { /∗ treść f() ∗/ } catch (x1) { throw. x2. Jabłonowski (MIM UW) PO i C++ 26 marca 2011 201 / 201 .Specyfikowanie wyjątków w interfejsie funkcji przykład void f() throw (x1..