wykład cpp

255
C++

Upload: chrymchrym2

Post on 21-Feb-2016

53 views

Category:

Documents


3 download

DESCRIPTION

WYkłady

TRANSCRIPT

Page 1: Wykład Cpp

C++

Page 2: Wykład Cpp

Kontak

• Łukasz Miądowicz

[email protected]

• 415 Gmach B

Page 3: Wykład Cpp

Literatura

• D. Ritchie, B. Kernighan “Język ANSI C”

• J. Grębosz “Symfonia C++ standard: programowanie w języku C++ zorientowane obiektowo”

• http://pl.wikibooks.org/wiki/C++

• http://c.learncodethehardway.org/book

• www.cplusplus.com

• Literatura dodatkowa:

• B. Stroustrup, “Język C++”

• B. Eckel “Thinking in C++”

Page 4: Wykład Cpp

Agenda wykładu część poświęcona językowi C

• Typy proste (całkowite, rzeczywiste zmienne).

• Operatory

• Wyrażenia

• Instrukcje

• Funkcje (definicja, deklaracja, składnia, wywołanie)

• Tablice

• Napisy

• Wskaźniki

• Struktury.

• Funkcje bibl. standardowej

Page 5: Wykład Cpp

Agenda wykładu część poświęcona językowi C++

• Język C, a C++

• Klasa

• Konstruktor / Destruktor

• Argumenty domyślne

• Przeciążanie (overloading) funkcji

• Referencje (w parametrach)

• Przeciążanie operatorów

• Funkcje zaprzyjaźnione

• Dynamiczne tworzenie obiektów

• Serializacja

• Rozdział na pliki

• Const-correctness

Page 6: Wykład Cpp

Środowiska/edytory developerskie

• Linux

• vi, vim, gedit, kate, xemacs, emacs, gcc/g++

• Code::Blocks

• KDevelop

• Eclipse CDT

• Windows

• Notepad (odradzam)

• Microsoft Visual Studio 20xx Express / Prof. / Premium / Ultimate

• Eclipse CDT

• cygwin

Page 7: Wykład Cpp

Instalacja środowiska Visual Studio 20xx dla Windows 8.0

• Link :

Page 8: Wykład Cpp

Tworzenie programu w C/C++ wygląda następująco:

• powstanie algorytmu (wymyślenie przepisu na ciasto, zestaw czynności jakie ma po kolei wykonywać program)

• etap programowania w C/C++ wynikowo dostajemy kod źródłowy w pliku np. nww.c lub nww.cpp

• etap kompilacji gcc nww.c lub g++ nww.cpp

• program w asemblerze

• kod maszynowy np. a.out lub a.exe (Windows)

• ładowanie i wykonanie programu

Page 9: Wykład Cpp
Page 10: Wykład Cpp

Struktura programu w C

dyrektywy preprocesoranagłówki funkcji / cała funkcja int main(){

deklaracje; instrukcje;

}ciało funkcji

Page 11: Wykład Cpp

Pierwszy program w C

Page 12: Wykład Cpp

Wypisanie wartości zmiennej na ekran

Page 13: Wykład Cpp

Wypisanie wielu zmiennych na ekran

Page 14: Wykład Cpp

Typy proste

• Typy całkowite

• char

• short int

• int

• long int

• long long int

• Typy rzeczywiste

• float

• double

• long double

• w C brak jest typu logicznego boolean

• do typów można używać signed, unsigned, <float.h>, <limits.h>

• char typ znakowy, mieszczący jeden znak (małe/duże litery oraz cyfry)

Page 15: Wykład Cpp

Typy zmiennych

• char - służy do przechowywania znaków

• int - służy do przechowywania liczb całkowitych

• float - służy do przechowywania liczb zmiennoprzecinkowych

• double - liczby zmiennoprzecinkowe podwójnej precyzji

• bool - zmienne logiczne (TRUE/FALSE)

• unsigned / signed (int | char ) - odpowiednio bez i ze znakiem

Łukasz Miądowicz, [email protected], FTiMS, PG

Page 16: Wykład Cpp

Typy zmiennych

Łukasz Miądowicz, [email protected], FTiMS, PG

Page 17: Wykład Cpp

Signed / unsigned

signed int - 4 bajty ; -2147483648 do 2147483647 unsigned int - 4 bajty; 0 do 4294967295

Page 18: Wykład Cpp

Typy zmiennych

Łukasz Miądowicz, [email protected], FTiMS, PG

Page 19: Wykład Cpp
Page 20: Wykład Cpp

Przykłady zmiennych różnych typów

Łukasz Miądowicz, [email protected], FTiMS, PG

Page 21: Wykład Cpp

Znaki specjalne

Łukasz Miądowicz, [email protected], FTiMS, PG

Page 22: Wykład Cpp

Typ char przykład

Page 23: Wykład Cpp

Identyfikatory

• Identyfikator to ciąg lister małych i dużych, znaku _ oraz cyfr. Identyfikator musi zaczynać się od litery. W języku C rozróżnia się wielkość liter.

Page 24: Wykład Cpp

Stałe i zmienne

Page 25: Wykład Cpp

Słowa kluczowe

auto break case char const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while

Page 26: Wykład Cpp

Priorytety operatorów

Page 27: Wykład Cpp

Priorytety operatorów - przykład

Page 28: Wykład Cpp

Operator warunkowy

• wynik = warunek ? wartość1 : wartość 2

Page 29: Wykład Cpp

Operator warunkowy

• jako makro

Page 30: Wykład Cpp

Bloki i instrukcje

• ; - kończy instrukcję (a nie rozdziela instrukcje jak w Pascalu)

• { } - instrukcja grupująca (blok)

• if, if else, switch - instrukcje wyboru

• while, do while, for - instrukcje pętli

• goto, continue, break, return - instrukcje skoku

Page 31: Wykład Cpp

Składnia warunku if

Page 32: Wykład Cpp

Warunek if przykład

Page 33: Wykład Cpp

Warunek if oraz else if

Page 34: Wykład Cpp

Switch

Page 35: Wykład Cpp
Page 36: Wykład Cpp

while

Najpierw oblicza wyrażenie. Jeśli jego wartość jest różna od zera (tzn. jest prawdziwe), to wykonuje się instrukcje

Page 37: Wykład Cpp
Page 38: Wykład Cpp

do while

Najpierw wykonuje instrukcję, a potem oblicza wyrażenie. Jeśli jego wartość jest <> 0 (tzn. jest prawdziwe), to ponownie wykonuje się instrukcję. Instrukcja wykona się co najmniej raz.

Page 39: Wykład Cpp
Page 40: Wykład Cpp

for

Na ogół ta instrukcja jest równoważna pętli

Page 41: Wykład Cpp
Page 42: Wykład Cpp
Page 43: Wykład Cpp
Page 44: Wykład Cpp

goto

Instrukcja goto nie może przekazywac sterowania do etykiety poza daną funkcją. Nie jest zalecane stosowanie goto.

Page 45: Wykład Cpp
Page 46: Wykład Cpp

break

Instrukcja break opuszcza najbardziej zagnieżdżoną instrukcję do, for, switch, whle

Page 47: Wykład Cpp
Page 48: Wykład Cpp

continue

Instrukcja ta przerywa wykonanie instrukcji wewnętrznej w najbardziej zagnieżdżonej pętli while, for, do. Powoduje następnie przejście do następnego krokku pętli. Nie dotyczy instrukcji switch.

Page 49: Wykład Cpp
Page 50: Wykład Cpp

return

kończy działanie funkcji i zwraca wartość;

Page 51: Wykład Cpp
Page 52: Wykład Cpp
Page 53: Wykład Cpp
Page 54: Wykład Cpp

Konwersje

C String - string w języku Cstd::string - string w stylu C++całkowite - int, long, shortzmiennoprzecinkowe - float, double

char -> (jednocyfrowe całkowite) (jednocyfrowe całkowite) -> char

jednocyfrowy C string -> char char -> (jednoznakowy) C string

Page 55: Wykład Cpp

(jednoznakowy) std::string -> char char -> (jednoznakowy) std::string

C string -> std::string std::string -> C string

Page 56: Wykład Cpp

C string -> całkowite całkowite -> C string

C string -> zmiennoprzecinkowe zmiennoprzecinkowe -> C string

Page 57: Wykład Cpp

liczba -> std::string std::string -> liczby

Page 58: Wykład Cpp

Zadania cz.1

1. Wczytaj liczbę naturalną n i następnie wyznacz wszystkie liczby pierwsze mniejsze od n [5 pkt.]2. Wczytaj trzy liczby całkowite a,b,c. Następnie policz wartości x1 oraz x2 będące rozwiązaniem równania kwadratowego ax^2+bx+c=0 [5 pkt.]3. Wykonaj zadanie ze SPOJ : pl.spoj.com/problems/JPESEL [5 pkt.]4*. Wykonaj zadanie ze SPOJ: spoj.com/problems/ONP [5 pkt.]

Page 59: Wykład Cpp

Funkcje

return wyrażenie - zwraca wartość wrażenia danego typujeżeli wstawimy za typ void wtedy funkcja nic nie zwracalista-parametrów - parametry można przekazywać przez wartość oraz przez wskaźnik

Page 60: Wykład Cpp
Page 61: Wykład Cpp
Page 62: Wykład Cpp

Przekazywanie parametrów do funkcji oraz zasięg zmiennych

Page 63: Wykład Cpp

Lista-parametrów jako void

• stosuje się taki zapis gdy chce się jawnie pokazać, że funkcja nie przyjmuje parametrów

Page 64: Wykład Cpp
Page 65: Wykład Cpp

Przeciążenia funkcji

Page 66: Wykład Cpp

Funkcje inline

Page 67: Wykład Cpp

Funkcje inline

• szybkość działania

• krótkie funkcje (kilka linii kodu)

• niewielka ilość miejsc wywołań funkcji

• przyrost długości pliku wykonywalnego

Page 68: Wykład Cpp

Tablice

typ tab[rozmiar]

w ten sposób definiujemy tablicę przechowującą zmienne typu typ o rozmiarze rozmiar.W C++ w odróżnieniu do języka Pascal elementy indeksuje się od 0.

typ tab[rozmiar1][rozmiar2]

w taki sposób deklarujemy tablicę dwuwymiarową i analogicznie n wymiarowąilość elementów w takiej tablicy to rozmiar1 * rozmiar2

Page 69: Wykład Cpp
Page 70: Wykład Cpp
Page 71: Wykład Cpp

Przekazywanie tablic do funkcji

Trzy równoważne sposoby deklarowania tablic

Przez kompilator w powyższych trzech zapisach nazwa tablicy zostanie zrozumiana jako wskaźnik do pierwszego jej elementu.

Page 72: Wykład Cpp
Page 73: Wykład Cpp

Losowanie liczb

Stosuję się dwie instrukcje, pierwsza (srand) ustawia punkt startowy, który jest stosowany do generowania serii liczb losowych całkowitych. Druga losuje liczbę (rand).

Page 74: Wykład Cpp
Page 75: Wykład Cpp

Argumenty linii poleceń

• program.exe arg1 arg2

• argc - ilość podanych parametrów

• argv[] - wskaźniki do argumentów

• argv[0] nazwa programu

• argv[1...n] kolejny argument

Page 76: Wykład Cpp

Argumenty funkcji main()

posiada dwa argumenty:- argc to liczba parametrów - argv to tablica 2D parametrów wywołania programu

Page 77: Wykład Cpp
Page 78: Wykład Cpp

Tablice wielowymiarowe

• Deklaracja

• Nadanie wartości początkowych

Page 79: Wykład Cpp

Tablice dwuwymiarowe

• Inna inicjalizacja

Page 80: Wykład Cpp

Preprocesor

• Program przetwarzający tekst w C/C++ przed jego kompilacją

• Nie sprawdza on poprawności składniowej i nic nie wie o języku C/C++ ani o języku maszynowym

• Dyrektywy p. nie są kompilowane stąd nie są zakończone średnikiem

• Dzięki niemu można zmieniać tekst programu “widziany” przez kompilator

• Dyrektywy to instrukcje preprocesora, rozpoczynające się od znaku #. Jedna dyrektywa to jedna linia. Jeżeli dyrektywa nie mieści się w jednej linii może być ona kontynuowana w nowej, trzeba wtedy zastosować znak ‘\’

Page 81: Wykład Cpp
Page 82: Wykład Cpp

Preprocesor

• Dyrektywa #include<file> lub #include “file” w miejscu wystąpienia włącza zawartość pliku file.

• Nie można stosować białych znaków pomiędzy <> / “”

• Dla nawiasów kątowych plik wyszukiwany jest w znanym preprocesorowi katalogu systemowym (zdefiniowanym w zmiennych globalnych systemu)

• Dla cydzysłowu plik wyszukiwany jest w katalogu bieżącym. Jeżeli pliku tam nie ma wtedy wyszukiwany jest w katalogu systemowym.

Page 83: Wykład Cpp

Preprocesor

• #define nazwa wartość

• zastępuje w tekście wystąpienie pełnego leksu nazwa daną wartością

Page 84: Wykład Cpp

Preprocesor

• #if #ifdef #ifndef #else #elif #endif

• dyrektywy kompilacji warunkowej tj. z włączeniem lub wyłączeniem pewnych fragmentów tekstu programu do tekstu wynikowego jaki zostanie przesłany do kompilacji

• konstrukcja przedstawia się następująco

Page 85: Wykład Cpp

Preprocesor

• Demo Ex1

• dla platformy Windows mamy makra __WINDOWS i WIN32, dla linuksa makra __linux i linux. Stosujemy to aby skompilować program na dwóch platformach

Page 86: Wykład Cpp

Preprocesor

• Zabezpieczenie przed wielokrotną kompilacją tego samego włączanego pliku. Taka sytuacja jest prawdopodobna, gdy w plikach które włączamy zagnieżdżone są instrukcje #include

Page 87: Wykład Cpp

Wskaźniki

• Wartością typu wskaźnikowego jest adres

• Demo #Ex 0

• Wskaźnik pusty w C/C++ to NULL (nie zawiera adresu żadnej zmiennej). Jest to makro rozwijane do ‘0’ lub ‘0L’ w czystym C (void*) 0

• Demo #Ex 0.1

Page 88: Wykład Cpp

Dynamiczna alokacja

Page 89: Wykład Cpp

Dynamiczna alokacja pamięci

Do alokacji dynamicznej tablicy używamy funkcję malloc zwraca ona wskaźnik do obszaru pamięci dla obiektu o rozmiarze rozmiar

Page 90: Wykład Cpp

Zadanie

Napisz program, który wylosuje 100 liczb całkowitych z zakresu od 3do 10 włącznie, wypisze te wartości na ekran, po czym zsumuje je i wynik wypisze na ekran. Wszelkie operacje mają zostać zrealizowane w oparciu o funkcje. Tablica ma być zadeklarowana w funkcji main. Oczekiwane funkcje: Wypełnienie tablicy o podanym rozmiarze losowymi liczbami z przedziału podawanego przez argumenty funkcji.Sumowanie określonej ilości liczb znajdujących się w tablicy i zwrot wyniku.Wypisywanie zawartości tablicy. [5 pkt.]

Page 91: Wykład Cpp

Wskaźniki

Wskaźnik to adres obiektu w pamięci komputera. Deklaracja wskaźnika, wskazującego na obiekt danego typu (typ) jest następująca:

Adres obiektu pobierany jest za pomocą jednoargumentowego operatora &.

Page 92: Wykład Cpp
Page 93: Wykład Cpp
Page 94: Wykład Cpp
Page 95: Wykład Cpp

Napisy

Stała napisowa w języku C jest tablicą znaków (char), która kończy się znakiem ‘\0’.

Page 96: Wykład Cpp
Page 97: Wykład Cpp

Kopiowanie napisów

W C napis to tablica znaków (char) + znak ‘\0’.

nap1 = nap2 skopiuje tylko wskaźniki

Page 98: Wykład Cpp
Page 99: Wykład Cpp

Wprowadzanie liczb

Page 100: Wykład Cpp

Funkcje printf()

Poniżej przedstawione są różne sposoby na formatowanie liczb

Page 101: Wykład Cpp

Typy wyliczeniowe

Typem danych dostępnym w C/C++ są wyliczenia (enumeracje/enumeration). Jest to zbiór nazwanych stałych całkowitych

Zmienne tego typu będą mogły przybierać dokładnie siedem wartości, wyliczonych kolejno w nawiasach klamrowych w definicji tego typu (stąd nazwa wyliczenia, typ wyliczeniowy).

Page 102: Wykład Cpp

elementom typu wyliczeniowego możemy nadać odpowiadające im wartości liczbowe “ręcznie”

Zasady:- pierwszy element (jawnie nie nadano mu wartości) ma wartość 0 pon==0- każdy inny typ ma wartość o jedną większą od poprzednika sob==1, nie==2

Page 103: Wykład Cpp

brak konwersji od int do dni, zmiennym typu dni można przypisywać wartości wyłącznie tego typu sob odpowiada wartość 1 to

Page 104: Wykład Cpp

ReferencjeSą to “przezwiska” zmiennych (prostych i złożonych). Odnośnik jest inną nazwą istniejącej zmiennej. Odnośnik w momencie inicjalizacji musi zostać skojarzony z jakąś zmienną. Później nie ma możliwości zmiany. Od tej pory nazwa odnośnika jak i nazwa tej zmiennej to nazwy równoważne. Wszelkie operacje na odnośnika odpowiadają operacjom na zmiennych.

Deklaracja odnośnika ma postać: Typ &ref = zmienna, gdzie zmienna musi już wcześniej istnieć

Page 105: Wykład Cpp

Ich główne zastosowanie to przesyłanie danych do funkcji i odwrotnie przesyłanie wyników z funkcji do funkcji wywołujących.

Page 106: Wykład Cpp

Stałe

Posiadają z góry ustalone wartości. const to final w Javie. Zmienną dowolnego typu można ustalić jako const. Wartości stałej nie można zmienić po jej utworzeniu

Typem ‘&i’ jest wskaźnik do ustalonej zmiennej całkowitej, a typem pi jest wskaźnik do (nieustalonej) zmiennej całkowitej.

Page 107: Wykład Cpp

wysyłamy do funkcji adres zmiennej nieustalonej, ale odpowiednim parametrem funkcji jest wskaźnik do stałej. Zapobiega to przypadkowym zmianom wartości zmiennej wewnątrz funkcji.

parametr zadeklarowany jest jako const int*, więc kompilator nie zgodzi się na zmianę wartości zmiennej wskazywanej przez pi. Tak więc wewnątrz funkcji zmienna skojarzona z parametrem typu

ustalonego jest traktowana jako stała niezależnie od tego czy odpowiedni argument wywołania był czy nie był ustalony.

Page 108: Wykład Cpp
Page 109: Wykład Cpp

Struktury

• Typ złożony. Dana zmienna typu strukturalnego może zawierać wiele danych (liczby, napisy, wskaźniki). Inaczej niż w przypadku tablicy dane te nie muszą zawierać tego samego typu

• Struktury w C to kolekcje danych typów składowych (mogą być różnych). Mogą zawierać inne typy strukturalne

• Definicja typu strukturalnego (typ nie zmienna):

• Konieczny jest średnik!

Page 110: Wykład Cpp

Struktury

• W C nazwa typu to struct Nazwa. Konieczne jest powtórzenie słowa struct przy użyciu tego typu. Możemy to obejść stosując specyfikator typedef:

• Nazwa jest aliasem struct Nazwa

• Gdy już mamy typ, możemy tworzyć zmienne tego typu. Składnia podobna jak dla innych typów

Page 111: Wykład Cpp

Struktury

• Istnieje możliwość definicji typu bezpośrednio za definicją, a przed średnikiem.

• Można również tworzyć obiekty anonimowe

Page 112: Wykład Cpp

Struktury

• Operator wyłuskania danych ze struktury to “.” (dla zmiennych strukturalnych statycznych ) np. a.skladowa

• Dla zmiennych strukturalnych dynamicznych to “->” np. a->skladowa

• Dla linii 3 i 5 konieczne są nawiasy gdyż operatory wyboru składowej (kropka i “strzałka”) posiadają wyższy priorytet niż operatory * i &

Page 113: Wykład Cpp

Struktury

• Gdy tworzymy obiekty typu C-struktury możemy od razu je zainicjować. Składowych może być mniej niż pól, wtedy pozostałe składowe zostaną zainicjowane zerami. Słowo struct może być pominięte w C++

• Demo Ex#2

• w C wszystkie zmienne lokalne muszą być zdefiniowane na początku, przed pierwszą instrukcją wykonywalną. Inaczej w C++

Page 114: Wykład Cpp

Struktury

• Weźmy funkcję biblioteczną ftime (dostępną w timeb.h). Są poza standardem C ale są dostępne w Linuxie. Funkcja ftime przyjmuje adres obiektu struktury timeb, która przedstawia się następującoFunkcja ftime wypełnia ten obiekt danymi. time - liczba sekund od początku epoki do chwili obecnej (time_t to typu całkowity ze znakiem, zwykle long) millitm - liczba milisekund od początku ostatniej sekundy timezone i dstflag nie są używane Za pomocą ftime możemy mierzyć czas wykonywania pewnych fragmentów kodu

• Demo #Ex3

Page 115: Wykład Cpp

Struktury

• Składową struktury / klasy nie może być obiekt tej klasy

• Może nią być natomiast wksaźnik do danej struktury / klasy

• Umożliwia to nam tworzenie list obiektów. W każdym obiekcie danej struktury jest składowa, która jest adresem następnego obiektu na liście. Jest to tak zwana lista jednokierunkowa.

• Demo Ex#4

• ostatni element, który nie ma następnika ma w tej składowej NULL (wskaźnik pusty)

Page 116: Wykład Cpp

Struktury

Page 117: Wykład Cpp

Dynamiczna alokacja C++

• Przy pomocy operatora new

• int* pi = new int;

• operator new zwraca adres pamięci początku typu

• Klasa* pk = new Klasa;

• Klasa* pk = new Klasa(1,2);

• int* pi = new int(18);

• const int* stala = new const int(1);

• int* pi = new int[1];

Page 118: Wykład Cpp

Wycieki pamięci

Page 119: Wykład Cpp

Obiektowy świat

• Najważniejszym pojęciem jest OBIEKT

• Tworząc obiekty i definiując ich nowe rodzeje można zbudować program

Page 120: Wykład Cpp

Czym jest obiekt?

Page 121: Wykład Cpp

Określenie obiektu

Page 122: Wykład Cpp

Obiekty mogą się różnić

Page 123: Wykład Cpp
Page 124: Wykład Cpp

Klasa

Page 125: Wykład Cpp

Składowe i ich dostępność

• Wszystkie składowe klasy są widoczne w jej wnętrzu, mogą mieć różny poziom dostępności z zewnątrz.

• Poziom ten jest określany przez słowa kluczowe: public, private, protected.

• W C++ występują sekcje

Page 126: Wykład Cpp

Różnice Klasa/struktura

• W klasach domyślnie wszystkie pola są prywatne

• W strukturach wszystkie składowe są publiczne

Page 127: Wykład Cpp

Składowe klasy

• public których nazwy mogą być używane we wszystkich miejscach programu, gdzie widoczna jest definicja klasy;

• prywatne ( private), których nazwy mogą być używane tylko przez funkcje, które same są składowymi tej samej klasy, lub funkcje z daną klasą zaprzyjaźnione (o czym dalej);

• chronione ( protected), których nazwy mogą być używane tylko przez funkcje, które same są składowymi tej samej klasy, funkcje z daną klasą zaprzyjaźnione, a także funkcje składowe i zaprzyjaźnione klas pochodnych (dziedziczących z) danej klasy.

Page 128: Wykład Cpp

Składowe klasy

• Dostępność wszystkich składowych klasy przez jej składowe funkcje (statyczne, niestatyczne, konstruktory, destruktor) dotyczy klasy, a nie konkretnych obiektów. Funkcja składowa klasy ma dostęp do pól niezależnie jakiego obiektu.

• Demo Ex#5

Page 129: Wykład Cpp

Pola klasy

• Deklaracje pól mają postać definicji zmiennych, ale nie mogą zawierać inicjalizacji: Sama definicja klasy nie powoduje utworzenia żadnych obiektów

Page 130: Wykład Cpp

Pola kolejność

• Pola można deklarować wewnątrz klasy w dowolnej kolejności i miejscu - przed lub po metodach; ich zakresem jest cała klasa, a nie tylko fragment następujący leksykalnie po definicji. Kolejność definicji pól ma jednak znaczenie podczas inicjowania i niszczenia (destrukcji) - pola są inicjowane w kolejności takiej, w jakiej były zadeklarowane, a usuwane w kolejności odwrotnej. Lepiej jednak unikać kodu, w którym kolejność ta ma znaczenie.

Page 131: Wykład Cpp

Metody

• Tak jak pola klasy opisują dane, które zawarte będą w każdym obiekcie klasy, tak metody definiują zbiór operacji, jakie na tych danych będzie można wykonywać.

Page 132: Wykład Cpp
Page 133: Wykład Cpp
Page 134: Wykład Cpp

Tablice obiektów• Obiekty klasy można grupować w tablice. Gdy nie ma metod.

• Demo Ex#6

• Gdy są metody:

• Tablicę obiektów takiej klasy można utorzyć bez jawnej inicjalizacji. Każdy element zostanie utworzony za pomocą konstruktora domyślnego. Zatem konstruktor domyślny musi dla takiej klasy istnieć!

• Druga możliwość to na liście inicjalizacyjnej tablicy - w nawiasach klamrowych - wywoływać jawnie konstruktory kreujące obiekty anonimowe

• Demo Ex#7

• istnieje konstruktor domyślny bo są parametry domyślne

Page 135: Wykład Cpp

Konstruktor

• To metoda wywoływana w czasie tworzenia każdego obiektu danej klasy:

• Konstruktor ma taką samą nazwę jak nazwa klasy

• nie zwraca żadnej wartości

• znajduje się w przestrzeni publicznej klasy

Page 136: Wykład Cpp
Page 137: Wykład Cpp
Page 138: Wykład Cpp

Destruktor

• Zwalnia pamięć

• Niszczy obiekt danej klasy

Page 139: Wykład Cpp
Page 140: Wykład Cpp

Destruktor

• Zasady tworzenia są podobe jak Konstruktora.

• Nazwa destruktora zaczyna się od ~ i nazwy klasy

Page 141: Wykład Cpp
Page 142: Wykład Cpp
Page 143: Wykład Cpp

Tworzenie obiektów

Page 144: Wykład Cpp

Dziedziczenie

• Definiując klasę pochodną definiujemy typ danych rozszerzający typ określany przez klasę bazową, a więc tę, z której klasa dziedziczy.

• Obiekty klasy pochodnej będą zawierać te składowe, które zawierają obiekty klasy bazowej, i, choć niekoniecznie, dodatkowe składowe, których nie było w klasie bazowej. Klasa pochodna może też dodawać nowe metody lub zmieniać implementację metod odziedziczonych ze swojej klasy bazowej.

• klasa bazowa jest bardziej ogólna, modeluje pewien fragment rzeczywistości na wyższym poziomie abstrakcji. Klasa pochodna jest bardziej szczegółowa, mniej abstrakcyjna. Tak więc, na przykład, pojęcie mebel jest bardziej abstrakcyjne, zaś krzesło bardziej konkretne: zatem klasa opisująca krzesła dziedziczyłaby z klasy opisującej meble: Mebel ← Krzeslo. Mogłaby dodać, na przykład, składową opisującą ilość nóg, której w bardziej ogólnej klasie Mebel nie było, bo nie każdy mebel ma nogi.

Page 145: Wykład Cpp

Dziedziczenie

• Klasy bazowe dla danej klasy deklarujemy na liście dziedziczenia umieszczonej po nazwie klasy i dwukropku, a przed definicją (ciałem) klasy. Klas bazowych może być kilka: ich nazwy umieszczamy wtedy na liście oddzielając je przecinkami. Powyższy zapis oznacza, że

• klasa A jest klasą pierwotną; nie dziedziczy z żadnej innej klasy (nie ma w C++ wspólnego „przodka” w rodzaju klasy Object z Javy);

• klasa B dziedziczy z A;

• klasa C dziedziczy z B, a więc pośrednio również z klasy A.

• Brak specyfikatora na liście dziedziczenia, jak w definicji klasy C z powyższego przykładu, jest równoważny z określeniem specyfikatora private.

Page 146: Wykład Cpp
Page 147: Wykład Cpp
Page 148: Wykład Cpp
Page 149: Wykład Cpp

Dziedziczenie

• Specyfikator public oznacza, że wszystkie składowe publiczne w klasie bazowej będą publiczne i w klasie pochodnej, a składowe chronione ( protected) będą chronione i w klasie dziedziczącej (składowe prywatne nie są widoczne);

• Specyfikator protected oznacza, że wszystkie składowe publiczne w klasie bazowej będą chronione ( protected) w klasie pochodnej, tak jak i składowe chronione z klasy bazowej.

• Atrybut protected oznacza, że dane pole czy metoda będą dostępne (tak jakby były publiczne) w klasach pochodnych danej klasy, a zachowują się jak prywatne dla wszystkich innych klas i funkcji.

• Specyfikator private oznacza, że wszystkie składowe publiczne i chronione z klasy bazowej stają się prywatnymi w klasie pochodnej.

• Nie są dziedziczone konstruktory i destruktor.

Page 150: Wykład Cpp

Dziedziczenie

• konstruktory nie są dziedziczone. Jeśli w klasie pochodnej nie zdefiniowaliśmy konstruktora, to zostanie użyty konstruktor domyślny. Aby jednak powstał obiekt klasy pochodnej, musi być najpierw utworzony podobiekt klasy nadrzędnej wchodzący w jego skład.

• Podobiekt klasy bazowej jest tworzony jeszcze przed wykonaniem konstruktora klasy pochodnej.

• Co w takim razie zrobić, aby do konstrukcji podobiektu klasy bazowej zawartego w tworzonym właśnie obiekcie klasy pochodnej użyć konstruktora innego niż domyślny? Wówczas musimy w klasie pochodnej zdefiniować konstruktor, a wywołanie właściwego konstruktora dla podobiektu klasy bazowej musi nastąpić poprzez listę inicjalizacyjną - wewnątrz konstruktora byłoby już za późno

Page 151: Wykład Cpp

• Na liście tej umieszczamy jawne wywołanie konstruktora dla podobiektu. Tak więc, jeśli klasą bazową jest klasa A i chcemy wywołać jej konstruktor, aby „zagospodarował” podobiekt tej klasy dziedziczony w klasie B, to na liście inicjalizacyjnej konstruktora klasy pochodnej B umieszczamy wywołanie A(...), gdzie w miejsce kropek wstawiamy oczywiście argumenty dla wywoływanego konstruktora. Wywołuje się tylko konstruktory bezpośredniej klasy bazowej („ojca”, ale nie „dziadka”; oczywiście konstruktor ojca poprzez swoją listę inicjalizacyjną może wywołać konstruktor swojego ojca...).

Page 152: Wykład Cpp

Dziedziczenie lista inicjalizacyjna• Klasa Pixel dziedziczy z klasy Point. Ponieważ

klasa Point nie miała konstruktora domyślnego, w klasie Pixel musimy przynajmniej jeden konstruktor zdefiniować i na jego liście inicjalizacyjnej wywołać jawnie konstruktor klasy Point z odpowiednimi argumentami

• Zauważmy, że nie wolno na liście inicjalizacyjnej konstruktora klasy pochodnej wymieniać nazw pól z klasy bazowej. W powyższym przykładzie na liście inicjalizacyjnej konstruktora klasy Pixel nie można umieścić wyrażenia x(x), bo składowa x pochodzi z klasy bazowej. Wolno natomiast, za pomocą wyrażenia color(color), zainicjować składową color, bo jest ona zadeklarowana w klasie pochodnej Pixel, a nie było jej w klasie bazowej. Składowe dziedziczone z klasy bazowej mogą być więc zainicjowane, ale tylko za pomocą jawnego wywołania konstruktora klasy bazowej z listy inicjalizacyjnej

Page 153: Wykład Cpp

Dziedziczenie

• Konstruktory wywoływane są w kolejności „od góry”: najpierw dla podobiektów klas bazowych, potem dla danej klasy. Jeśli klasa dziedziczy z kilku klas, to konstruktory klas bazowych są wywoływane w kolejności ich wystąpienia na liście dziedziczenia.

• Tak będzie również dla bardziej rozbudowanej hierarchii dziedziczenia. Jeśli, na przykład, klasa C dziedziczy z B, która z kolei dziedziczy z A, to podczas tworzenia obiektu klasy C najpierw zostanie utworzony obiekt A, następnie obiekt klasy B zawierający jako podobiekt utworzony już obiekt A, a dopiero na końcu obiekt klasy C zawierający jako podobiekt utworzony obiekt klasy B.

Page 154: Wykład Cpp

Dziedziczenie

• destruktory wywoływane są w kolejności odwrotnej do konstruktorów.

• podczas niszczenia obiektu najpierw wywoływany jest jego destruktor (oczywiście, jeśli jest zdefiniowany). Następnie usuwane są obiekty będące składowymi tego obiektu nie odziedziczonymi z klas bazowych. Jeśli są to składowe obiektowe, to oczywiście nastąpi wywołanie dla nich destruktorów, jeśli były zdefiniowane. Następnie wywoływany jest destruktor dla podobiektu bezpośredniej klasy bazowej, potem usuwane są obiekty będące niedziedziczonymi składowymi tego podobiektu i tak dalej.

• Demo Ex#8

• najpierw tworzony jest podobiekt klasy A, a co za tym idzie wywoływany jest konstruktor tej klasy. Następnie tworzony jest obiekt klasy B. Obiekt ten ma składową obiektową typu K. Widzimy, że najpierw konstruowana jest ta składowa, a co za tym idzie wywoływany jest konstruktor klasy K, a dopiero potem wywoływany jest konstruktor klasy B. Dopiero na samym końcu wywoływany jest konstruktor klasy C.

• Kolejność wywoływania destruktorów jest dokładnie odwrotna. W szczególności podczas niszczenia podobiektu klasy B najpierw wywoływany jest destruktor tej klasy, a dopiero potem niszczona jest jego składowa obiektowa klasy K.

Page 155: Wykład Cpp

Klasa string

• aby z niej skorzystać trzeba #include<string>, nie <string.h> C-style

• można deklarować zmienne string bez nadawania jak i z nadawaniem im wartości poczatkowych

Page 156: Wykład Cpp

Klasa string

• posiada funkcję size

• dwa napisy mogą zostać porównane dzięki operatorowi ==

Page 157: Wykład Cpp

Klasa string

• stringi mogą być łączone dzięki operatorowi + (konkatenacja)

• do napisów można odwoływać się za pomocą indexów, ale nie można przekroczyć długości napisu

Page 158: Wykład Cpp

Klasa vector

• aby jej użyć trzeba #include<vector>

• jest podobny do tablic, natomiast zawiera kilka dodatkowych funkcji

• przekazywany jest przez referencję lub przez wartość do funkcji

• nie ma sprawdzenia zakresu (wektor nie ma ograniczenia rozmiaru z góry)

Page 159: Wykład Cpp

Klasa vector

• zmienna typu vector deklarowana jest z typem elementów oraz rozmiarem

• aby dostać się do wartości elementów klasy wektor używamy indeksów

Page 160: Wykład Cpp

Klasa vector

• Klasa vector posiada funkcję size - zwracającą rozmiar vectora

Page 161: Wykład Cpp

Klasa vector

• dwa wektory mogą zostać porównane operatorem == (są równe jeżeli mają ten sam rozmiar oraz odpowiednie elementy są sobie równe)

• funkcja może zwracać wektor (tablic nie może zwracać)

Page 162: Wykład Cpp

Klasa vector

• wektor może być przekazany do funkcji poprzez wartość lub poprzez referencję

Page 163: Wykład Cpp

Strumienie

Wejście / wyjście w C++ obsługujemy przy użyciu strumieni (strumieni informacji w postaci bajtów płynących od źródła do ujścia). Informacje płyną do programu ze źródła lub od programu do ujścia.

Dedykowane strumieniom są klasy zawierające odpowiednie pola i metody

Page 164: Wykład Cpp
Page 165: Wykład Cpp
Page 166: Wykład Cpp
Page 167: Wykład Cpp
Page 168: Wykład Cpp
Page 169: Wykład Cpp

Demo #Ex

Page 170: Wykład Cpp
Page 171: Wykład Cpp

Przeciążenia

• Przeciążanie albo przeładowanie nazwy funkcji polega na zdefiniowaniu kilku funkcji o takiej samej nazwie.

• Funkcje przeciążone muszą się różnić listą argumentów – kompilator rozpoznaje po argumentach, o którą wersję danej funkcji chodzi.

• Możemy przeciążać również funkcje składowe i konstruktory w klasie.

• Przykład przeciążenia konstruktora: class Punkt{ double x, y; public: Punkt () { x = y = 0; } Punkt (double xx, double yy) { x=xx; y=yy; }

Page 172: Wykład Cpp

Argumenty stałe

● Modyfikator const może występować przy argumentach w funkcji. ● Jeśli argument jest stały to argumentu takiego nie wolno w funkcji

zmodyfikować. ● Argumentami stałymi są najczęściej argumenty przekazywane przez

referencje. ● Przykład funkcji z argumentami stałymi:int min (const int &a, const int &b) { return a<b ? a : b; }

● Argument stały jest inicjalizowany przy wywołaniu funkcji.

Page 173: Wykład Cpp

Referencja do stałej jako argument w funkcji

● Referencja do stałej może się odnosić do obiektu zewnętrznego (może być zadeklarowany jako stały) ale również do obiektu tymczasowego.

● Przykład referencji do stałej:const int &rc = (2*3-5)/7+11;

● Przykład argumentu funkcji, który jest referencją do stałej:int fun (const int &r); // wywołanie może mieć postać // fun(13+17); // gdzie argumentem może być wyrażenie

Page 174: Wykład Cpp

Pola stałe w klasie

● W klasie można zdefiniować pola stałe z deklaratorem const. Przykład:class Zakres { const int MIN, MAX; public: Zakres(int m); // … };

● Inicjalizacji pola stałego (i nie tylko stałego) można dokonać tylko poprzez listę inicjalizacyjną w konstruktorze (po dwukropku za nagłówkiem). Przykład:Zakres :: Zakres(int mi, int ma) : MIN(mi), MAX(ma) { if (MIN<0||MIN>=MAX) throw string("złe zakresy"); } Inicjalizacja pól na liście ma postać konstruktorową.

● Konstruktor kopiujący nie zostanie wygenerowany automatycznie tylko wtedy, gdy w klasie nie ma pól stałych.

Page 175: Wykład Cpp

Metody stałeDana metoda nie zmienia stanu obiektu, na rzecz którego została wywołana, nie zmienia żadnej jego składowej. Deklaracja takiej metody następuje poprzez dodanie słowa const za nawiasem zamykającym listę parametrów, a przed średnikiem w deklaracji, nawiasem otwierającym ciało funkcji. Deklaracja stałości wystepuje w definicji i deklaracji metody

Metoda stała nie może zmieniać składowych obiektu, na rzecz którego została wywołana, ale może zmienić składowe innych obiektów tej samej klasy, do których ma dostęp.

Page 176: Wykład Cpp

Stałe funkcje składowe

● W klasie można zadeklarować stałe funkcje składowe z deklaratorem const. Przykład:class Zakres { const int MIN, MAX; public: int min () const; int max () const; // … };

● Stała funkcja składowa gwarantuje nam, że nie będzie modyfikować żadnych pól w obiekcie (nie zmieni stanu obiektu). Przykład:int Zakres::min () const { return MIN; } int Zakres::max () const { return MAX; }

● Na obiektach stałych możemy działać tylko stałymi funkcjami składowymi.

Page 177: Wykład Cpp

Na rzecz obiektu stałego można wywoływać wyłącznie metody

zadeklarowane jako stałe.

Tu obiekt stały p1 jest odnośnikowym argumentem

metody jawnie nie gwarantuje, że go nie zmieni (tym razem chodzi o

obiekt przekazywany jako argument, a nie obiekt na rzecz którego metodę wywołujemy!). Żeby to wywołanie było

prawidłowe, parametr funkcji powinien był być zadeklarowany jako ' const Punkt&', co jednak w

naszym przykładzie jest niemożliwe, bo funkcje przesun

właśnie zmieniają obiekt przesłany jako argument.

próbujemy ją wywołać na rzecz obiektu stałego. Takie wywołanie jest nielegalne: obiekt jest stały, więc nie można na jego rzecz wywołać metody, która jawnie

nie gwarantuje jego niemodyfikowalności - taką

gwarancją jest właśnie zadeklarowanie metody jako

stałej.

Page 178: Wykład Cpp

Pola zawsze modyfikowalne

● Jeśli obiekt zostanie zadeklarowany jako stały, to można na nim wywoływać tylko stałe funkcje składowe, które nie zmieniają stanu obiektu.

● W klasie można jednak zdefiniować zawsze modyfikowalne pola składowe za pomocą deklaratora mutable. Przykład:class Zakres { mutable int wsp; public: void nowyWsp (int w) const; // … };

● Pole zawsze modyfikowalne może być zmieniane w stałym obiekcie przez stałą funkcję składową. Przykład:void Zakres::nowyWsp (int w) const { if (w<0||w<wsp/2||w>wsp*2) throw string("zły współczynnik"); wsp = w; }

Page 179: Wykład Cpp

Stos/Sterta

• Dane możemy przechowywać w różnych obszarach pamięci.

• Mamy dwie możliwości:

• Stos - zapisywane są na nim zmienne lokalne (bez użycia operatora new). W momencie wejścia do bloku (np. funkcji) - znak { - zmienne są kolejno tworzone i umieszczane na stosie. W momencie wyjścia programu stos } jest opróżniany. Zmienne są usuwane w kolejności odwrotnej do ich stworzenia i w ten sposób stos jest zwalniany.

• Sterta - obszar pamięci wolnej - programista może tworzyć w jej obszarze zmienne służące do umieszczania w nich danych (create,add,delete, edit). W C/C++ te dane będą istnieć do końca działania programu wcześniej zajęty obszar będzie więc cały czas zajęty. Może to prowadzić do sytuacji problematycznych jak wycieki pamięci. (Przykład)

Page 180: Wykład Cpp

Operator new

• dynamicznie alokuję 4 bajty pamięci (int). Adres zmiennej zapamiętujemy w zmiennej typu wskaźnikowego. New zwraca adres.

• Usunięcie wskaźnika do zaalokowanej pamięci nie zwalnia tej pamięci. Pozostaje ona w dalszym ciągu “zajęta”

Page 181: Wykład Cpp

Operator new

• dynamicznie alokuję 4 bajty pamięci (int). Adres zmiennej zapamiętujemy w zmiennej typu wskaźnikowego. New zwraca adres.

• Usunięcie wskaźnika do zaalokowanej pamięci nie zwalnia tej pamięci. Pozostaje ona w dalszym ciągu “zajęta”

Page 182: Wykład Cpp

Operator delete

• argumentem operatora jest zmienna wskaźnikowa pi.

• operatora delete nie wolno zastosować do adresu który nie został uprzednio zwrócony przez operator new.

• operatora delete nie można zastosowac dwa razy do tego samego adresu.

• powyższa instrukcja zwalnia pamięć przydzieloną tablicy

Page 183: Wykład Cpp

Destruktor

• Wywoływany automatycznie podczas destrukcji obiektu.

• Niszczenie obiektów lokalnych odbywa się w momencie dojścia programu do końca bloku kodu. W tym momencie niszczone są zarówno zmienne lokalne, jak i zmienne obiektowe.

• Usuwanie obiektów ze stosu odbywa się w odwrotnej kolejności do ich tworzenia - jako pierwsze zniszczone zostaną te które zostały utworzone jako ostatnie

Page 184: Wykład Cpp

Destruktor

• Nazwa destruktora musi być taka sama jak nazwa klasy poprzedzona znakiem tyldy ~

• Destruktor nie ma zdefiniowanego typu zwracanego (nawet void)

• Destruktor musi być bezparametrowy (co implikuje, że nie może być przeciążany)

• Zachowanie destruktora podobne jest do metod, tyle tylko, że jest wywoływany automatycznie podczas usuwania obiektu z pamięci. Jawne wywołanie destruktora poniżej.

Page 185: Wykład Cpp

Destruktor

• Nazwa destruktora musi być taka sama jak nazwa klasy poprzedzona znakiem tyldy ~

• Destruktor nie ma zdefiniowanego typu zwracanego (nawet void)

• Destruktor musi być bezparametrowy (co implikuje, że nie może być przeciążany)

• Zachowanie destruktora podobne jest do metod, tyle tylko, że jest wywoływany automatycznie podczas usuwania obiektu z pamięci. Jawne wywołanie destruktora poniżej.

Page 186: Wykład Cpp

Konstruktor kopiujący

• Definiuje się go w klasach, które zawierają pola wskaźnikowe (dla takiego przypadku definiujemy też destuktor)

• Jego zadaniem jest utworzenie obiektu identycznego jak inny, który istniał wcześniej. Obiekt ma być identyczny, ale z oryginałem całkowicie rozłączny. Późniejsza modyfikacja jednego nie wpływa na modyfikację drugiego obiektu.

• argumentem tutaj jest obiekt, który istniał wcześniej, zostanie przesłany do niego przez odniesienie.

Page 187: Wykład Cpp

Konstruktor kopiujący

Page 188: Wykład Cpp

Konstruktor kopiujący

• Automatycznie wygenerowany konstruktor kopiuje obiekt-wzorzec na nowo tworzony obiekt “pole po polu”. Jest to tak zwane kopiowanie płytkie: jeśli istnieją pola wskaźnikowe np. dynamicznie tworzone tablice to elementem obiektu jest wtedy tylko wskaźnik, a nie cała tablica, a więc kopiowane są te wskaźniki, a nie obiekty przez nie wskazywane. W takich przypadkach program powinien dostarczyć właściwy konstruktor kopiujący robiący kopiowanie głębokie

Page 189: Wykład Cpp

Konstruktor kopiujący

Marnotrawienie pamięci. Lepiej alokować w konstruktorze

pamięć na imię

Alokujemy odpowiednią ilość miejscai następnie kopiujemy nazwę

przekazaną jako parametr. +1 bo ‘\0’

Page 190: Wykład Cpp

Klasy zagnieżdżone

Page 191: Wykład Cpp

Constructor chaining

• Kiedy inicjalizujemy nowy obiekt, implicite wywoływany jest konstruktor. Zobaczmy dwie sytuacje, które często sprawiają programistom problem:

• Czasem chcemy aby klasa posiadała konstruktor, który robi jedną rzecz ale dodatkowo chcemy aby zrobił coś jeszcze ekstra. Proces w którym wywoływany jest w jednym konstruktorze drugi nazywany jest constructor chaining. Niektóre języki pozwalają na jego realizację natomiast nie C++. Program się skompiluje ale przeważnie nie będzie działał dobrze. Spędzisz dużo czasu na debuggowaniu programu.

• Oczywiście można skopiować część kodu z jednego konstruktora do drugiego, posiadanie zduplikowanego kodu zaśmieca jednak klasę przez co staje się mniej zrozumiała.

Page 192: Wykład Cpp

Constructor chaining

• Rozwiązaniem tego problemu jest zastosowanie w konstruktorach wywołań metod.

Page 193: Wykład Cpp

Dziedziczenie

• Klasa pochodna zawiera te informacje co klasa bazowa plus dodatkowe ekstra informacje.

• Klasa bazowa jest bardziej ogólna. Klasa pochodna jest bardziej szczegółowa, mniej abstrakcyjna. Na przykład pojęcie mebel jest bardziej abstrakcyjne niż krzesło. Krzesło jest bardziej konkretne: zatem klasa opisująca krzesła dziedziczy po meble Mebel <- Krzesło. Krzesło może dodać składową opisującą np. ilość nóg. Bo nie każdy mebel ma nogi

Page 194: Wykład Cpp

Dziedziczenie

• klasy bazowe dla danej klasy deklarujemy na liście dziedziczenia, która umieszczona jest za nazwą klasy i dwukropku a przed ciałem klasy. Klas bazowych może być kilka (wielodziedziczenie) wtedy kolejne oddzielamy od siebie przecinkiem. Powyższy zapis oznacza:

• klasa A jest klasą pierwotną: nie dziedziczy z żadnej klasy

• klasa B dziedziczy z A

• klasa C dziedziczy z B, a więc pośrednio również z A

Page 195: Wykład Cpp

Dziedziczenie

Page 196: Wykład Cpp

Dziedziczenie

Page 197: Wykład Cpp

Metody wirtualne

• W klasie pochodnej można zdefiniować metodę o sygnaturze i typie zwracanym takich samych jak dla metody w klasie bazowej. Jest to tak zwane przesłonięcie

• Zdefiniujmy:

Page 198: Wykład Cpp

Metody wirtualne

• Wywołujemy fun za pomocą a, pa, pab, raa, rab. Pytamy która z metoda A czy B zostanie wywołana.

Page 199: Wykład Cpp

Wiązania

• Dla wywołań na rzecz obiektów klas niepolimorficznych odpowiednia metoda jest wybierana już w czasie kompilacji na podstawie typu statycznego. Tzw. wczesne wiązanie (early binding)

• Typ dynamiczny obiektu wskazywanego prezz wskaźnik lub odniesienie może być dopiero określony w momencie wykonania programu - Późne wiązanie (late binding)

Page 200: Wykład Cpp

• Aby definiowana klasa była polimorficzna, wystarczy jeśli choć jedna metoda tej klasy będzie wirtualna. W szczególności może to być destruktor (ale nie konstruktor - ten wirtualny nie może być nigdy).

• Deklaracja metody jako wirtualnej musi mieć miejsce w klasie bazowej. Jeśli metoda została zadeklarowana w klasie bazowej jako wirtualna, to wersje przesłaniające tę metodę we wszystkich klasach pochodnych (nie tylko „synach”, ale i „wnukach”, „prawnukach”,...) są też wirtualne. Ponowne deklarowanie ich w klasach pochodnych jako wirtualnych jest dopuszczalne i zalecane, bo zwiększa czytelność kodu, ale w zasadzie zbędne.

Page 201: Wykład Cpp

Wyłączenie polimorfizmu

Page 202: Wykład Cpp

Klasy abstrakcyjne

• To klasy w których pewne metody w ogóle nie są zdefiniowane, a tylko zadeklarowane. Takie metody powinny być oczywiście wirtualne - w dziedziczących klasach muszą być dostarczone konkretne implementacje tych metod, aby można było tworzyć ich obiekty. Obiektów samej klasy abstrakcyjnej tworzyć nie można, bo nie jest to klasa do końca zdefiniowana. Zazwyczaj służy tylko jako definicja interfejsu, czyli zbioru metod jakie chcemy implementować na różne sposoby w klasach dziedziczących.

• Można natomiast tworzyć obiekty klas pochodnych (nieabstrakcyjnych), w których metody wirtualne zadeklarowane ale nie zdefiniowane w klasie bazowej zostały przesłonięte konkretnymi implementacjami (klasy takie stają się wtedy konkretne). Co bardzo ważne, do takich obiektów można się odnosić poprzez wskaźniki i odniesienia o typie statycznym abstrakcyjnej klasy bazowej.

Page 203: Wykład Cpp

• Metodę wirtualną można zadeklarować jako czysto wirtualną pisząc po nawiasie kończącym listę argumentów ' = 0', na przykład:

• W ten sposób informujemy kompilator, że definicji tak zadeklarowanej metody może w ogóle nie być, a zatem cała klasa, w której tę metodę zadeklarowano, będzie abstrakcyjna.

Page 204: Wykład Cpp
Page 205: Wykład Cpp

Funkcje zaprzyjaźnione

• Funkcja, która jest przyjacielem klasy, ma dostęp do wszystkich jej prywatnych i chronionych składowych.

• To klasa deklaruje, które funkcje są jej przyjaciółmi.

• Deklaracja przyjaźni może się pojawić w dowolnej sekcji i jest poprzedzona słowem kluczowym friend.

Page 206: Wykład Cpp

• Przykład klasy z funkcją zaprzyjaźnioną: // klasa z funkcją zaprzyjaźnionąclass pionek{ int x, y; // … friend void raport (const pionek &p); }; // funkcja, która jest przyjacielem klasy void raport (const pionek &p) { cout << "(" << p.x << ", " << p.y << ")"; }

Page 207: Wykład Cpp

• Nie ma znaczenia, w której sekcji (prywatnej, chronionej czy publicznej) pojawi się deklaracja przyjaźni.

• Funkcja zaprzyjaźniona z klasą nie jest jej składową, nie może używać wskaźnika this w stosunku do obiektów tej klasy.

• Jedna funkcja może się przyjaźnić z kilkoma klasami.

• Istotą przyjaźni jest dostęp do niepublicznych składowych w klasie – sensowne jest deklarowanie przyjaźni, gdy dana funkcja pracuje z obiektami tej klasy.

Page 208: Wykład Cpp

• Można także umieścić w klasie nie tylko deklarację funkcji zaprzyjaźnionej, ale również jej definicję; tak zdefiniowana funkcja:

• jest nadal tylko przyjacielem klasy;

• jest inline;

• może korzystać z typów zdefiniowanych w klasie.

• Funkcją zaprzyjaźnioną może być funkcja składowa z innej klasy.

Page 209: Wykład Cpp

• Możemy w klasie zadeklarować przyjaźń z inną klasą, co oznacza, że każda metoda tej innej klasy jest zaprzyjaźniona z klasą pierwotną.

• Przykład: class A{ friend class B; // … };

• Przyjaźni się nie dziedziczy.

Page 210: Wykład Cpp

• Dwie klasy mogą się przyjaźnić z wzajemnością: class A;class B{ friend class A; // … }; class A{ friend class B; // … };

Page 211: Wykład Cpp

Moduły

• Każdy większy program składa się z pewnej liczby oddzielnych części – modułów.

• Moduł to kompletny fragment programu (moduł obliczeniowy, moduł we/wy, moduł prezentacji, itp).

• Podział kodu na moduły porządkuje logikę programu.

• Należy minimalizować zależności między modułami.

Page 212: Wykład Cpp

Biblioteki

• Moduły, z których może korzystać wiele programów umieszcza się w oddzielnych skompilowanych plikach, zwanych bibliotekami.

• Typy bibliotek w C++:

• biblioteka statyczna jest dołączana do programu wynikowego na etapie linkowania;

• biblioteka współdzielona jest dołączana do programu w trakcie ładowania programu do pamięci;

• biblioteka dynamiczna jest dołączana do uruchomionego procesu w trakcie działania programu.

Page 213: Wykład Cpp

Biblioteki

• Biblioteka to zbiór klas, funkcji i zmiennych, z których mogą korzystać różne programy.

• Biblioteka ma postać binarną – jej poszczególne fragmenty są skompilowane (biblioteka jest kolekcją plików obiektowych).

• Korzystanie z bibliotek ułatwia programowanie (korzystamy z gotowych i sprawdzonych fragmentów kodu) i przyspiesza proces rekompilacji.

Page 214: Wykład Cpp

Biblioteki

• Wynikiem samej kompilacji pliku źródłowego (plik.c albo plik.cpp) jest plik plik.o pod Linuxem albo plik.obj pod Windowsem.

• Biblioteki statyczne mają nazwy libmodul.a pod Linuxem albo modul.lib pod Windowsem.

• Biblioteki dynamiczne mają nazwy libmodul.so pod Linuxem (tak jak biblioteki współdzielone) albo modul.dll pod Windowsem.

Page 215: Wykład Cpp

Biblioteka statyczna

• Używając biblioteki statycznej przekazujemy jej archiwum linkerowi w czasie kompilacji. Linker wyszukuje w nim tylko tych plików obiektowych, które są niezbędne do działania naszego programu i kopiuje je bezpośrednio do programu.

• Program wynikowy korzystający z biblioteki statycznej jest obszerniejszy ale szybciej się ładuje do pamięci.

• Program wynikowy zlinkowany z biblioteką statyczną jest niezależny od plików zewnętrznych.

• Uaktualnienie biblioteki wymaga rekompilacji programu.

Page 216: Wykład Cpp

Biblioteka statyczna

Page 217: Wykład Cpp

Biblioteka współdzielona

• Programy korzystające biblioteki współdzielonej nie zawierają bezpośrednio kodu z tej biblioteki a tylko referencję do niej.

• Program wynikowy korzystający z biblioteki współdzielonej jest chudszy ale wolniej ładuje się do pamięci (biblioteki współdzielone są odszukiwane i ładowane do pamięci razem z programem).

• Program wynikowy skompilowany z biblioteką współdzieloną jest zależny od plików zewnętrznych.

• Zmodyfikowanie biblioteki współdzielonej spowoduje zmianę w działaniu programu ale bez jego ponownej kompilacji.

Page 218: Wykład Cpp

Biblioteka współdzielona

Page 219: Wykład Cpp

Biblioteka dynamiczna

• Programy korzystające bibliotek dynamicznych nie zawierają bezpośrednio kodu z tej biblioteki ale muszą korzystać ze specjalnych metod włączania takich bibliotek w trakcie działania programu (plik nagłówkowy <dlfcn.h>).

• Program wynikowy korzystający z biblioteki dynamicznej jest chudszy i szybciej ładuje się do pamięci, ale działa wolniej (biblioteki dynamiczne można załadować w dowolnym momencie w trakcie działania programu).

• Program wynikowy skompilowany z biblioteką dynamiczną jest zależny od plików zewnętrznych.

• Zmodyfikowanie biblioteki dynamicznej spowoduje zmianę w działaniu programu ale bez jego ponownej kompilacji.

Page 220: Wykład Cpp

Biblioteka dynamiczna

Page 221: Wykład Cpp

Tworzenie bibliotek

• Tworzenie programu bez dołączanych bibliotek.

• Załóżmy, że mamy pliki src1.cpp, src2.cpp i src3.cpp, które stanowią moduł obliczeniowy oraz plik prog.cpp, który będzie korzystał z funkcji i klas zdefiniowanych w module obliczeniowym.

• Aby skompilować cały program razem z modułem obliczeniowym należy wydać polecenie: $ g++ -Wall -ansi -pedantic src1.cpp src2.cpp src3.cpp prog.cpp -o calculation

• Aby skompilować cały program razem z modułem obliczeniowym i statycznie zlinkować z innymi bibliotekami (rozmiar programu wynikowego będzie znacznie większy) należy wydać polecenie: $ g++ -static …

• Aby uruchomić skompilowany program należy wydać polecenie: $ ./calculation

• Aby sprawdzić z jakimi bibliotekami jest linkowany program i jakie symbole są w nim użyte należy wydać polecenie: $ ldd calculation $ nm calculation

Page 222: Wykład Cpp

Tworzenie bibliotek

• Program korzystający z biblioteki statycznej.

• Najpierw kompilujemy pliki źródłowe do plików obiektowych: $ g++ -c -Wall -ansi -pedantic src1.cpp src2.cpp src3.cpp

• Następnie łączymy pliki obiektowe do jednego pliku bibliotecznego libsrc.a: $ ar crs libsrc.a src1.o src2.o src3.o

• Na koniec należy skompilować plik z programem i zlinkować go z biblioteką: $ g++ -c -Wall -ansi -pedantic prog.cpp $ g++ -o calculation prog.o –L. –lsrc

• Teraz można uruchomić skompilowany program: $ ./calculation

• Wyjaśnienie:

• opcja -Lścieżka określa ścieżkę do biblioteki,

• opcja -lbiblioteka określa nazwę biblioteki.

Page 223: Wykład Cpp

Tworzenie bibliotek

• Program korzystający z biblioteki współdzielonej.

• Najpierw kompilujemy pliki źródłowe z opcją -fpic do plików obiektowych: $ g++ -fpic –c -Wall -ansi -pedantic src1.cpp src2.cpp src3.cpp

• Następnie łączymy pliki obiektowe do jednego pliku bibliotecznego libsrc.so: $ g++ –fpic -shared -o libsrc.so src1.o src2.o src3.o

• Na koniec należy skompilować plik z programem i wlinkować do niego informacje o bibliotece: $ g++ -Wall -ansi -pedantic prog.cpp $ g++ -o calculation prog.o –L. –lsrc

• Przed uruchomieniem programu trzeba zapisać w zmiennej środowiskowej LD_LIBRARY_PATH ścieżkę do biblioteki: $ export LD_LIBRARY_PATH="LD_LIBRARY_PATH:ścieżka"

• Teraz można uruchomić skompilowany program: $ ./calculation

Page 224: Wykład Cpp

Tworzenie bibliotek

• Program korzystający z biblioteki dynamicznej.

• Bibliotekę dynamiczną przygotowujemy tak samo jak bibliotekę współdzieloną libsrc.so: $ g++ -fpic –c -Wall -ansi -pedantic src1.cpp src2.cpp src3.cpp $ g++ –fpic -shared -o libsrc.so src1.o src2.o src3.o

• Na koniec należy skompilować plik z programem i dołączyć do niego dynamicznego linkera opcją -ldl: $ g++ -Wall -ansi -pedantic prog.cpp $ g++ -o calculation prog.o –ldl

• Teraz można uruchomić skompilowany program: $ ./calculation

• Wyjaśnienie:

• aby skorzystać z dynamicznego linkera należy do programu włączyć plik nagłówkowy <dlfcn.h>;

• aby załadować dynamiczną bibliotekę trzeba skorzystać z funkcji dlopen, dlsym, dlerror i dlclose.

Page 225: Wykład Cpp

Definicja przestrzeni nazw

• Przestrzeń nazw tworzymy za pomocą słowa kluczowego namespace, ograniczając zawartość klamrami:namespace przestrzeń{ // deklaracje i definicje }

• Aby odnieść się do typu, funkcji albo obiektu umieszczonego w przestrzeni nazw musimy stosować kwalifikator zakresu przestrzeń:: poza tą przestrzenią.

• Funkcja main() musi być globalna, aby środowisko uruchomieniowe rozpoznało ją jako funkcję specjalną.

• Do nazw globalnych odnosimy się za pomocą pustego kwalifikatora zakresu ::, na przykład ::wspolczynnik.

• Jeśli w przestrzeni nazw zdefiniujemy klasę to do składowej statycznej w takiej klasie odnosimy się kwalifikując najpierw nazwą przestrzeni a potem nazwą klasy przestrzeń::klasa::składowa.

Page 226: Wykład Cpp

Definicja przestrzeni nazw

● Przykład przestrzeni nazw:namespace wybory { int min2 (int, int); int min3 (int, int, int); } int wybory::min2 (int a, int b) { return a<b ? a : b; } int wybory::min3 (int a , int b , int c) { return min2(min2(a,b),c); } int min4 (int a, int, b, int c, int d) { return wybory::min2( wybory::min2(a,b), wybory::min2(c,d)); }

Page 227: Wykład Cpp

Deklaracja użycia

• Deklaracja użycia wprowadza lokalny synonim nazwy z innej przestrzeni nazw (wskazanej nazwy można wówczas używać bez kwalifikowania jej nazwą przestrzeni).

• Deklaracja użycia using ma postać: using przestrzeń::symbol;

• Deklaracja użycia obowiązuje do końca bloku, w którym wystąpiła.

• Deklaracje użycia stosujemy w celu poprawienia czytelności kodu.

• Deklaracje użycia należy stosować tak lokalnie, jak to jest możliwe.

• Jeśli większość funkcji w danej przestrzeni nazw korzysta z jakiejś nazwy z innej przestrzeni, to deklaracje użycia można włączyć do przestrzeni nazw.

Page 228: Wykład Cpp

Dyrektywa użycia

• Dyrektywa użycia udostępnia wszystkie nazwy z określonej przestrzeni nazw.

• Dyrektywa użycia using namespace ma postać:using namespace przestrzeń;

• Dyrektywy użycia stosuje się najczęściej w funkcjach, w których korzysta się z wielu symboli z innej niż ta funkcja przestrzeni nazw.

• Globalne dyrektywy użycia są stosowane do transformacji kodu i nie powinno się ich stosować do innych celów.

• Globalne dyrektywy użycia w pojedynczych jednostkach translacji (w plikach .cpp) są dopuszczalne w programach testowych czy w przykładach, ale w produkcyjnym kodzie jest to niestosowne i jest uważane za błąd.

• Globalnych dyrektyw użycia nie wolno stosować w plikach nagłówkowych!

Page 229: Wykład Cpp

Anonimowe przestrzenie nazw

• Anonimową przestrzeń nazw tworzymy za pomocą słowa kluczowego namespace bez nazwy, ograniczając zawartość klamrami: namespace{ // deklaracje i definicje }

• Anonimowa przestrzeń nazw zastępuje użycie deklaratora static przy nazwie globalnej – dostęp do nazw zdefiniowanych w przestrzeni anonimowej jest ograniczony do bieżącego pliku.

• Dostęp do anonimowej przestrzeni nazw jest możliwy dzięki niejawnej dyrektywie użycia. namespace $$${ // deklaracje i definicje } using namespace $$$;W anonimowej przestrzeni nazw $$$ jest unikatową nazwą w zasięgu, w którym jest zdefiniowana ta przestrzeń.

Page 230: Wykład Cpp

Przeszukiwanie nazw

• Gdy definiujemy funkcję z jakiejś przestrzeni nazw (przed nazwą definiowanej właśnie funkcji stoi kwalifikator przestrzeni) to w jej wnętrzu dostępne są wszystkie nazwy z tej przestrzeni.

• Funkcja z argumentem typu T jest najczęściej zdefiniowana w tej samej przestrzeni nazw co T. Jeżeli więc nie można znaleźć funkcji w kontekście, w którym się jej używa, to szuka się jej w przestrzeniach nazw jej argumentów.

• Jeżeli funkcję wywołuje metoda klasy K, to pierwszeństwo przed funkcjami znalezionymi przez typy argumentów mają metody z klasy K i jej klas bazowych.

Page 231: Wykład Cpp

Aliasy przestrzeni nazw

• Jeżeli użytkownicy nadają przestrzeniom nazw krótkie nazwy, to mogą one spowodować konflikt. Długie nazwy są niewygodne w użyciu. Dylemat ten można rozwiązać za pomocą krótkiego aliasu dla długiej nazwy przestrzeni nazw.

• Aliasy dla przestrzeni nazw tworzymy za pomocą słowa kluczowego namespace z dwiema nazwami namespace krótka = długa_nazwa_przestrzeni;

• Przykład: namespace American_Telephone_and_Telegraph{ // tutaj zdefiniowano Napis} namespace ATT = American_Telephone_and_Telegraph;American_Telephone_and_Telegraph::Napis n = "x";AAT::Napis nn = "y";

• Nadużywanie aliasów może prowadzić do nieporozumień!

Page 232: Wykład Cpp

Komponowanie i wybór

• Interfejsy projektuje się po to, by zminimalizować zależności pomiędzy różnymi częściami programu. Minimalne interfejsy prowadzą do systemów łatwiejszych do zrozumienia, w których lepiej ukrywa się dane i implementację, łatwiej się je modyfikuje oraz szybciej kompiluje.

• Eleganckim narzędziem do konstruowania interfejsów są przestrzenie nazw.

Page 233: Wykład Cpp

Komponowanie i wybór

● Gdy chcemy utworzyć interfejs z istniejących już interfejsów to stosujemy komponowanie przestrzeni nazw za pomocą dyrektyw użycia, na przykład:namespace His_string { class String { /* ... */ }; String operator+ (const String&, const String&); String operator+ (const String&, const char*); void fill (char) ; // ... } namespace Her_vector { template<class T> class Vector { /* ... */ }; // ... } namespace My_lib { using namespace His_string; using namespace Her_vector; void my_fct(String&) ; }

● Dyrektywa użycia wprowadza do zasięgu wszystkie deklarację z podanej przestrzeni nazw.

Page 234: Wykład Cpp

Komponowanie i wybór

● Teraz przy pisaniu programu można posługiwać się My_lib:void f () { My_lib::String s = "Byron"; // znajduje My_lib::His_string::String // ... } using namespace My_lib; void g (Vector<String> &vs) { // ... my_fct(vs[5]); // ... }

Page 235: Wykład Cpp

Komponowanie i wybór

● Gdy chcemy utworzyć interfejs i dołożyć do niego kilka nazw z innych interfejsów to stosujemy wybór za pomocą deklaracji użycia, na przykład:namespace My_string { using His_string::String; using His_string::operator+; // … }

● Deklaracja użycia wprowadza do zasięgu każdą deklarację o podanej nazwie. Pojedyncza deklaracja użycia może wprowadzić każdy wariant funkcji przeciążonej.

Page 236: Wykład Cpp

Komponowanie i wybór

• Łączenie komponowania (za pomocą dyrektyw użycia) z wyborem (za pomocą deklaracji użycia) zapewnia elastyczność potrzebną w praktyce. Z użyciem tych mechanizmów możemy zapewnić dostęp do wielu udogodnień, a zarazem rozwiązać problem konfliktu nazw i niejednoznaczności wynikających z komponowania.

• Nazwy zadeklarowane jawnie w przestrzeni nazw (łącznie z nazwami wprowadzonymi za pomocą deklaracji użycia) mają pierwszeństwo przed nazwami wprowadzonymi za pomocą dyrektyw użycia.

• Nazwę w nowej przestrzeni nazw można zmienić za pomocą instrukcji typedef lub poprzez dziedziczenie.

Page 237: Wykład Cpp

Przestrzenie nazw są otwarte

• Przestrzeń nazw jest otwarta, co oznacza, że można do niej dodawać nowe pojęcia w kilku deklaracjach (być może rozmieszczonych w różnych plikach), na przykład: namespace NS { int f (); // NS ma nową składową f() } namespace NS { int g (); // teraz NS ma dwie składowe f() i g() }

• Definiując wcześniej zadeklarowaną składową w przestrzeni nazw, bezpieczniej jest użyć operatora zakresu niż ponownie otwierać przestrzeń (kompilator nie wykryje literówek w nazwie składowej), na przykład: namespace NS { int h (); } int NS::hhh () // błąd - zamiast h napisano hhh { /*…*/ }

Page 238: Wykład Cpp

Przestrzeń nazw std

• W języku C++ wszystkie nazwy z biblioteki standardowej są umieszczone w przestrzeni nazw std.

• W języku C tradycyjnie używa się plików nagłówkowych i wszystkie nazwy w nich deklarowane są w przestrzeni globalnej (dostępne bez żadnych kwalifikacji).

• Aby zapewnić możliwość kompilowania przez kompilatory C++ programów napisanych w C przyjęto, że jeśli użyjemy tradycyjnej (pochodzącej z C) nazwy pliku nagłówkowego, to odpowiedni plik jest włączany i zadeklarowane w nim nazwy są dodawane do globalnej przestrzeni nazw. Jeśli natomiast ten sam plik nagłówkowy włączymy pod nową nazwą, to nazwy w nim deklarowane są dodawane do przestrzeni nazw std. Przyjęto przy tym konwencję, że pliki nagłówkowe z C nazwie nazwa.h są w C++ nazywane cnazwa (pary plików <math.h> i <cmath>, itp).

Page 239: Wykład Cpp

Ogólne spojrzenie na wyjątki

● Wyjątki zaprojektowano do wspierania obsługi błędów. ● System wyjątków dotyczy zdarzeń synchronicznych – na

przykład do kontroli zakresu czy błędów we/wy. ● Mechanizm obsługi wyjątków można traktować jako

nielokalną strukturę sterującą, która korzysta ze zwijania stosu.

Page 240: Wykład Cpp

Obsługa błędów

● Wyjątek to obiekt sygnalizujący błąd. ● Wyjątki można zgłaszać po wykryciu sytuacji krytycznej

instrukcją throw. ● Wyjątki zgłoszone w bloku try można wyłapać w bloku catch i obsłużyć sytuację problemową.

● Wyjątki w sposób naturalny dzielą kod na część obliczeniową i część sterującą obliczeniami.

Page 241: Wykład Cpp

Zgłaszanie i łapanie wyjątków

● Wyjątek to obiekt dowolnego typu. ● Kod wykrywający błąd zgłasza wyjątek instrukcją throw, na przykład:

throw 15; throw "problem z dokładnością obliczeń"; throw moja_klasa(7.532);

● Chęć złapania wyjątku sygnalizuje się umieszczeniem kodu w instrukcji try-catch. ● Wynikiem działania throw jest zwinięcie stosu, aż do znalezienia odpowiedniego

bloku catch (w funkcji, która bezpośrednio lub pośrednio wywołała funkcję zgłaszająca wyjątek).

Page 242: Wykład Cpp

Zgłaszanie i łapanie wyjątków

● Przykład zgłoszenia i obsługi błędu:try { int x = 0; cerr << "integer (>0) "; cin >> x; if (!cin) throw "i/o error"; if (x<=0) throw x; // … } catch (const char *ex) { cerr << "number format error" << endl;} catch (int ex) { cerr << "number value error" << endl;}

Page 243: Wykład Cpp

Zgłaszanie i łapanie wyjątków

• Program może obsługiwać tylko wyjątki zgłaszane w bloku try.

• Po zgłoszeniu wyjątku, sterowanie nie wraca już do miejsca zgłoszenia.

• Po obsłużeniu wyjątku w bloku catch sterowanie przenoszone jest za instrukcję try-catch.

• Wyjątki są rozróżniane po typie.

Page 244: Wykład Cpp

Grupowanie wyjątków

● Wyjątki często w sposób naturalny tworzą rodziny (zastosowanie dziedziczenia w strukturalizacji wyjątków).

● Przykład hierarchii wyjątków:class BladMat {}; class Nadmiar : public BladMat {}; class Niedomiar : public BladMat {}; class DzielZero : public BladMat {};

● Przykład organizacji rozpoznawania wyjątków:try { // throw …; } catch (DzielZero) {/*…*/} catch (BladMat) {/*…*/}

● Kolejność bloków catch ma znaczenie przy rozpoznawaniu wyjątków.

Page 245: Wykład Cpp

Dopasowywanie wyjątków

• Rozważmy przykład: try{ throw E; } catch (H) { // kiedy się tutaj znajdziemy? }

• H jest tego samego typu co E;

• H jest jednoznaczną publiczną klasą bazową dla E;

• H i E są wskaźnikami, a dla typów na które wskazują zachodzi 1) lub 2);

• H jest referencją, a dla typu do którego się odnosi zachodzi 1) lub 2).

Page 246: Wykład Cpp

Złapanie każdego wyjątku

• Można wyłapać każdy wyjątek blokiem catch(...).

• Blok catch(...) może wystąpić tylko jako ostatni blok.

• W bloku catch(...) nie jest znany typ wyjątku.

• W bloku catch można powtórnie zgłosić ten sam wyjątek który właśnie został wyłapany instrukcją throw bez argumentów.

• Zgłoszenie innego wyjątku w bloku catch można traktować jak podmianę wyjątku (można zmienić nie tylko wartość ale i typ zgłaszanego wyjątku).

Page 247: Wykład Cpp

Zagnieżdżanie

• Instrukcję try-catch można umieścić w bloku try – wtedy niewyłapane wyjątki w wewnętrznej instrukcji try-catch będą zgłoszeniem wyjątku w zewnętrznym bloku try.

• Instrukcję try-catch można również umieścić w bloku catch – wtedy niewyłapane wyjątki w wewnętrznej instrukcji try-catch będą traktowane jak zgłoszenie wyjątku w zewnętrznym bloku catch.

Page 248: Wykład Cpp

Implementacja mechanizmu rzucania i łapania wyjątków

• Odwikłanie stosu – wielkie sprzątanie.

• Po zgłoszeniu instrukcją throw obiekt wyjątku jest umieszczany w pamięci globalnej w specjalnie do tego przeznaczonym miejscu.

• Wyjątek uznaje się za obsłużony w momencie jego wyłapania przez jakiś blok catch, ale dopiero przy wyjściu z tego bloku wyjątek jest likwidowany.

• Nie wolno rzucać wyjątków, gdy inny wyjątek jest w trakcie lotu.

Page 249: Wykład Cpp

Wyjątki w konstruktorach

• Nie wolno zgłaszać wyjątków w destruktorach, bo to może powodować problemy przy odwikłaniu stosu.

• Gdy wyjątek zostanie zgłoszony w konstruktorze, to obiekt nie zostanie utworzony.

• Gdy wyjątek zostanie zgłoszony w konstruktorze w trakcie inicjalizacji części odziedziczonej lub obiektu składowego, to zainicjalizowana część zostanie automatycznie zlikwidowana.

• Gdy chcemy zgłosić wyjątek w ciele konstruktora, to najpierw należy zwolnić zasoby przydzielone w ciele konstruktora.

Page 250: Wykład Cpp

Zdobywanie zasobów

• Problem: kiedy funkcja na początku rezerwuje zasób (otwiera strumień, przydziela pamięć, ustawia klucz kontroli dostępu, itp), to może go na końcu nie zwolnić, gdy po drodze zostanie zgłoszony wyjątek.

• Rozwiązanie: zarządzanie zasobami poprzez opakowywanie ich klasami.

• Schemat postępowania:

• w konstruktorze klasy opakowującej zasób zostaje zarezerwowany (gdy rezerwacja się nie powiedzie zostaje zgłoszony wyjątek);

• klasa opakowująca udostępnia narzędzia do korzystania z zasobu;

• w destruktorze klasy opakowującej zasób zostaje zwolniony (zadziała również w przypadku zwijania stosu przy zgłoszonym wyjątku).

Page 251: Wykład Cpp

Zdobywanie zasobów

● Przykład zdobywania zasobów poprzez inicjalizację:class plik { FILE *wsk; public: plik (const char *naz, const char *atr) { wsk = fopen(naz,atr); if (!wsk) throw brak_pliku; } ~plik () throw() { fclose(wsk); wsk = 0; } operator FILE* () throw() { return wsk; } };

Page 252: Wykład Cpp

Zdobywanie zasobów

● Wzorzec klasy auto_ptr wspiera technikę zdobywania zasobów poprzez inicjalizację – jego definicja znajduje się w pliku nagłówkowym <memory>.

● Obiekt auto_ptr jest inicjalizowany wskaźnikiem i można się nim posługiwać w programie jak wskaźnikiem.

● Konstruktor i przypisanie wzorca auto_ptr zapewniają niejawną konwersję z auto_ptr<P> do auto_ptr<B> o ile można dokonać konwersji z P* do B*.

● Po skopiowaniu jednego obiektu auto_ptr na drugi, obiekt źródłowy na nic już nie wskazuje.

● W destruktorze auto_ptr zapewnione jest wywołanie operatora delete na wskazywany obiekt.

Page 253: Wykład Cpp

Zdobywanie zasobów

● Przykład użycia auto_ptr (zakładamy, że okrąg dziedziczy po figura):okrąg *po = new okrąg(123.0); // … auto_ptr<okrąg> apo1 = po; // teraz apo1 jest odpowiedzialny za usunięcie obiektuapo1->r = 123.456; // odniesienie do składowej r w okręgu auto_ptr<okrąg> apo2 = apo1; // przeniesienie własności z apo1 na apo2 // i teraz apo2 jest odpowiedzialny za usunięcie obiektufigura *pf = apo2.get(); // wyłuskanie wskaźnika z auto_ptr // uwaga, teraz apo1.get()==0 auto_ptr<figura> apf = apo2; // przeniesienie własności i konwersja typu // i teraz apf jest odpowiedzialny za usunięcie obiektu

Page 254: Wykład Cpp

Wyjątki oraz new i delete

● Funkcje użyte do implementacji operatorów new i delete są zadeklarowane w <new>. Deklaracja tych operatorów jest następująca:void * operator new (size_t) throw(bad_alloc);void operator delete (void *) throw(); void * operator new[] (size_t) throw(bad_alloc);void operator delete[] (void *) throw();

● Operator new (oraz new[]) zgłasza wyjątek bad_alloc, gdy nie uda się zarezerwować pamięci na obiekt (tablicę obiektów).

● Istnieje deklaracja obiektu, który powoduje, że operator new nie zgłasza wyjątku, tylko przekazuje wskaźnik pusty:struct nothrow_t {}; extern const nothrow_t nothrow; Stworzono też specjalne wersje operatora new z parametrem nothrow_t, które zapobiegają zgłaszaniu wyjątków:void * operator new (size_t, const nothrow_t &) throw(); void * operator new[] (size_t, const nothrow_t &) throw();

Page 255: Wykład Cpp

§

● Przykład użycia nothrow przy alokacji pamięci:int *p = new int[1000000]; // może zgłosić wyjątek bad_alloc // … // poniższy kod nie zgłosi wyjątku if (int *q = new(nothrow) int[1000]) { // przydział się powiódł } else { //przydział nie powiódł się }

● Funkcja uncaught_exception() zwraca true, gdy wyjątek zgłoszono ale jeszcze nie wyłapano – umożliwia to specyfikowanie różnych działań w destruktorze zależnie od tego, czy obiekt jest niszczony normalnie, czy w ramach zwijania stosu.