Wyklad CPP 3


Obiektowe programowanie w języku C++
Kurs podstawowy
Dr inż. Lucjan Miękina
upel.agh.edu.pl/wimir/login/
Katedra Robotyki i Mechatroniki
March 19, 2013
1/14
Obiektowe programowanie w języku C++
Klasy zagnieżdżone
Można umieścić deklarację klasy
wewnątrz deklaracji innej klasy.
1 # include < iostream >
2 class List { Deklaracja klasy wewnętrznej po-
3 // prywatna deklaracja klasy wewn .
zostaje ukryta na zewnątrz klasy ją
4 class Element {
zawierającej. W związku z tym, używa
5 int Value ; // dane przech . w elem .
się zagnieżdżonych deklaracji klas w
6 Element * pNext ;
7 public :
przypadku, gdy reprezentują one część
8 Element ( int v , Element * next ) {
funkcjonalności wymaganą tylko w
9 Value = v; pNext = next ;
klasie zawierającej.
10 }
11 Element * Next () { return pNext ; }
1 # include " Element .h"
12 void Info () {
2
13 std :: cout << "\ nValue : " << Value ;
3 int main () {
14 }
4 List list ;
15 }; // koniec kl . Element
5 list . insert (1);
16 Element * First ;
6 list . insert (2);
17 public :
7 list . insert (3);
18 List () { First = 0; };
8 list . Info ();
19 void insert ( int v) {
9 return 0;
20 First = new Element (v , First ); };
10 }
21 void Info () {
22 Element * p = First ;
23 while (p) { Rezultat działania:
24 p -> Info ();
1 lm@arch > ./ a. out
25 p=p -> Next ();
2 Value : 3
26 } }
3 Value : 2
27 }; // koniec kl . List
4 Value : 1
2/14
Obiektowe programowanie w języku C++
Tablice i ciągi obiektów
Typy obiektowe (klasy) stosuje się identycznie w konstrukcji tablic i ciągów obiektów.
1 # include < stdio .h >
2 # include " Complex .h"
3 # define CNT 2
4
1 # include < stdio .h >
5 int main () {
2 # include " Complex .h"
6 printf (" Create array of objects :" );
3 # define CNT 2
7 int i;
4
8 Complex arr [ CNT ];
5 int main () {
9 for (i =0; i < CNT ; i ++) {
6 printf (" Create sequence of objects :" );
10 arr [i ]. SetRe (i );
7 int i;
11 arr [i ]. SetIm (i );
8 Complex * pArr = new Complex [ CNT ];
12 }
9 if ( pArr ) {
13 printf ("\ nTheir info :" );
10 for (i =0; i < CNT ; i ++) {
14 for (i =0; i < CNT ; i ++)
11 ( pArr +i)-> SetRe (i );
15 arr [i ]. Info ();
12 ( pArr +i)-> SetIm (i );
16 printf ("\nDe - allocate " );
13 }
17 return 0;
14 printf ("\ nTheir info :" );
18 }
15 for (i =0; i < CNT ; i ++)
16 ( pArr +i)-> Info ();
1 Create array of objects :
17 printf ("\ nDe - allocate :" );
2 NN : 0 x3cdf8d30 , (0.0 , 0.0) created
18 delete [] pArr ;
3 NN : 0 x3cdf8d50 , (0.0 , 0.0) created
19 }
4 Their info :
20 return 0;
5 NN : 0 x3cdf8d30 , (0.0 , 0.0)
21 }
6 NN : 0 x3cdf8d50 , (1.0 , 1.0)
7 De - allocate
8 NN : 0 x3cdf8d50 , (1.0 , 1.0) deleted
9 NN : 0 x3cdf8d30 , (0.0 , 0.0) deleted
3/14
Obiektowe programowanie w języku C++
Dziedziczenie
Jest to mechanizm, który umożliwia definiowanie nowych typów jako potomków już
istniejącego typu obiektowego. Funkcjonuje jeszcze inne określenie tej relacji: klasa
bazowa - klasa pochodna.
Istotą dziedziczenia jest przejmowanie pól i metod przodka przez jego potomka, z
możliwością dodania własnych pól i metod lub zmiany znaczenia metod przodka (tzw.
przedefiniowanie lub pokrycie). Przedefiniowanie polega na napisaniu metody o takiej
samej nazwie i takim samym zbiorze parametrów formalnych, ale działającej
odmiennie, a więc modyfikującej zachowanie obiektu. Metoda przedefiniowana w klasie
potomnej może mieć dostęp do metody oryginalnej z nadklasy, np. w celu wywołania
jej jako pewnej fazy swego działania (by nie powtarzać raz napisanego kodu).
Relacja generalizacja-specjalizacja: klasa potomna jest tworzona w celu konkretyzacji
funkcjonowania obiektu. Osiąga się to przez wprowadzenie nowych pól (dodatkowe
parametry) i metod (dodatkowe lub zmienione operacje). Jest to zatem mechanizm
coraz większej specjalizacji obiektu.
Metody wirtualne stosuje się w celu wykorzystania kolejnego ważnego mechanizmu
obiektowego - polimorfizmu. Polega on na zdefiniowaniu w obrębie ciągu klas od
bazowej do pochodnej, rodziny metod o takich samych nazwach i zestawach
parametrów formalnych, lecz o innej treści (a więc zmodyfikowanym działaniu). Każda
z metod ma w nagłówku słowo kluczowe virtual i jej adres jest przechowywany w
tablicy metod wirtualnych VMT. W zależności od tego na rzecz jakiego obiektu dana
metoda jest wywołana, następuje wybór odpowiedniego jej aspektu. Odbywa się to
dynamicznie - w czasie działania programu. Stąd mówi się o dynamicznym wiązaniu.
4/14
Obiektowe programowanie w języku C++
Dziedziczenie
Relację generalizacja-specjalizacja na schematach struktury obiektowej przedstawia się
specjalnym symbolem.
Klasy TMessage, TTrigData, TDataVector i TDataDiscrete są klasami pochodnymi
(potomkami) klasy bazowej TDataSegment (przodka).
Klasy TTimeData, TFreqOctData i TFreqNBData są klasami pochodnymi
(potomkami) klasy TDataVector.
5/14
Obiektowe programowanie w języku C++
Dziedziczenie - klasy pochodne
Klasą pochodną jest klasa, która wywodzi się od innych klas, zwanych jej klasami
bazowymi. Definicja klasy pochodnej ma ogólną postać:
1 class Nazwa : Lista_dziedziczenia
2 {
3 // Cialo klasy , w poznanej postaci
4 };
gdzie Lista_dziedziczenia jest ciągiem nazw zdefiniowanych wcześniej klas, każda z
tych nazw może być poprzedzona kwalifikatorem wirtualności (słowo kluczowe virtual)
lub dostępności (słowa kluczowe public, protected lub private).
Jeśli nazwa pewnej klasy bazowej występuje w liście dziedziczenia klasy pochodnej, to
każdy obiekt klasy pochodnej składa ze wszystkich składników tej klasy bazowej
(dziedziczonych) i dodatkowo z własnych składników  pól i metod.
Jeśli posłużono się kwalifikatorem wirtualności, dana klasa bazowa jest odziedziczona
wirtualnie  w obiekcie klasy pochodnej występuje tylko jeden podobiekt (pole)
reprezentujący tę klasę bazową.
Reguły dostępności składników klasy bazowej
prywatne składniki klasy bazowej są niedostępne w klasach pochodnych, niezależnie od
sposobu dziedziczenia tej klasy jako całości.
przy dziedziczeniu prywatnym (private) wszystkie składniki klasy bazowej stają się
prywatnymi w klasie pochodnej
przy dziedziczeniu chronionym (protected) składniki chronione klasy bazowej pozostają
chronionymi w klasie pochodnej, a składniki publiczne stają się chronionymi,
dziedziczenie publiczne klasy bazowej nie zmienia dostępności składników w klasie lub klasach
pochodnych  składniki publiczne pozostają publicznymi, a chronione pozostają chronionymi.
6/14
Obiektowe programowanie w języku C++
Dziedziczenie - klasy pochodne
Klasa CDerived dziedziczy z trzech klas bazowych: wirtualnie i publicznie z klasy
CBase1, w sposób chroniony z klasy CBase2 i prywatnie z klasy CBase3.
1 class CDerived :
2 virtual public CBase1 ,
3 protected CBase2 ,
4 private CBase3
5 {
6 // deklaracje wlasnych pol i metod , jak w kazdej klasie
7 };
Sens takiej deklaracji jest następujący: klasa CDerived łączy w sobie funkcjonalność
zawartą w trzech klasach z których dziedziczy, a dodatkowo dostarcza funkcjonalności
zdefiniowanej wewnątrz siebie  za pomocą własnych pól i metod.
Konstruktory klas pochodnych
Konstruktory te, poza inicjatorami własnych pól mogą zawierać inicjatory
podobiektów, czyli obiektów klas bazowych zawartych w danej klasie. Inicjator
podobiektu ma postać:
1 Base ( lista_argumentow_inicjatora )
gdzie Base jest nazwą klasy bazowej, a lista_argumentow_inicjatora to ciąg wartości
oddzielonych przecinkami, zgodny z listą argumentów odpowiedniego konstruktora
klasy Base. Wartości te są nadawane poszczególnym argumentom konstruktora klasy
Base, który w tym miejscu będzie wywoływany.
7/14
Obiektowe programowanie w języku C++
Dziedziczenie - klasy pochodne
Kolejność tworzenia obiektu złożonego
1
tworzenie i inicjalizacja obiektów odziedziczonych wirtualnie
2
w kolejności określonej przez listę dziedziczenia tworzy się i inicjuje wszystkie
podobiekty niewirtualnych klas bazowych
3
w kolejności określonej przez deklaracje pól klasy tworzy się i wstępnie inicjuje
wszystkie pola obiektu
4
wykonuje się ciało konstruktora, gdzie ostatecznie inicjuje się (jeśli to konieczne)
wybrane pola obiektu
Destruktory klas pochodnych
Niszczenie obiektu zachodzi w kolejności odwrotnej do jego tworzenia:
1
najpierw wykonuje się ciało destruktora
2
następnie w kolejności odwrotnej do określonej przez deklaracje pól klasy niszczy
się jej pola obiektowe
3
następnie w kolejności odwrotnej do określonej przez listę dziedziczenia niszczy
się wszystkie podobiekty niewirtualnych klas bazowych
4
następnie w kolejności odwrotnej do przyjętej w trakcie tworzenia obiektu niszczy
się wszystkie podobiekty wirtualnych klas bazowych
8/14
Obiektowe programowanie w języku C++
Dziedziczenie - przykład
1 # include < iostream > 1 # include " P2D .h"
2 using namespace std ; 2
3 3 // klasa pochodna
4 // klasa bazowa 4 class P3D : public P2D {
5 class P2D { 5 int Z;
6 protected : 6 public :
7 int X , Y; 7 P3D ( int x , int y , int z) :
8 public : 8 P2D (x , y), Z(z) {
9 P2D ( int x , int y) : X(x), Y(y) { 9 }
10 } 10 void Info ( void ) {
11 virtual void Info ( void ) { 11 cout << endl << " ";
12 cout << endl << " " 12 P2D :: Info ();
13 << endl << " " << X << " " 13 cout << endl
14 << endl << " " << Y << " " 14 << " " << Z << " "
15 << endl << " "; 15 << endl << " ";
16 } 16 }
17 }; 17 };
1 # include " P3D .h"
1
2
2 1
3 main () {
3 1
4 // dekl . obiektu p2 klasy P2D
4
5 P2D p2 (1 , 1);
5
6 p2 . Info ();
6
7 // dekl . obiektu p3 klasy P3D
7 2
8 P3D p3 (2 , 2, 2);
8 2
9 p3 . Info ();
9
10 return 0;
10 2
11 }
11
9/14
Obiektowe programowanie w języku C++
Dziedziczenie - przykład nieco większej hierarchii klas
1 # include < iostream > 1 # include " Zwierze .H"
2 # ifndef _ZWIERZE_H 2 # ifndef _PIES_H
3 # define _ZWIERZE_H 3 # define _PIES_H
4 class Zwierze { 4 class Pies : public Zwierze {
5 protected : 5 public :
6 char Glos [16]; 6 Pies () : Zwierze () {
7 public : 7 strcpy ( Glos , " HAU !" );
8 Zwierze () { 8 }
9 } 9 void DajGlos () {
10 virtual void DajGlos () { 10 for ( int i = 0; i < 3; i ++)
11 std :: cout << Glos ; 11 Zwierze :: DajGlos ();
12 } 12 }
13 }; 13 };
14 # endif /* _ZWIERZE_H */ 14 # endif /* _PIES_H */
1 # include " Pies .H"
1 # include " Zwierze .H" 2 # ifndef _PIESMYSLI_H
2 # ifndef _KOT_H 3 # define _PIESMYSLI_H
3 # define _KOT_H 4 // pies mysliwski
4 5 class PiesMysli : public Pies {
5 class Kot : public Zwierze { 6 int IloscZdobyczy ;
6 public : 7 public :
7 Kot () : Zwierze () { 8 PiesMysli () : Pies () {
8 strcpy ( Glos , " MIAU !" ); 9 IloscZdobyczy = 0;
9 } 10 strcpy ( Glos , " HAUuUu !" );
10 }; 11 }
11 # endif /* _KOT_H */ 12 };
13 # endif /* _PIESMYSLI_H */
10/14
Obiektowe programowanie w języku C++
Dziedziczenie - metody wirtualne i wskazniki polimorficzne w akcji
1 # include < stdlib .h >
2 # include " Kot .H"
3 # include " PiesMysli .H"
4 int main ( int argc , char * argv []) {
5 // tablica wskaznikow typu klasy bazowej
6 Zwierze * rejestr [1024];
7 for ( int i = 0; i < 1024; i ++)
8 rejestr [i] = 0; // inicjalizacja wsk . zero
9 Pies * p = new Pies ();
10 rejestr [0] = p;
11 Kot * k = new Kot ();
12 rejestr [1] = k;
13 PiesMysli * pm = new PiesMysli ();
14 rejestr [2] = pm ;
15 // niech wszystkie dadza kolejno glos
16 for ( int i = 0; i < 1024; i ++)
17 if ( rejestr [i] != 0)
18 rejestr [i]-> DajGlos ();
19 // usuwanie obiektow z pamieci
20 for ( int i = 0; i < 1024; i ++)
21 if ( rejestr [i] != 0)
22 delete rejestr [i ];
23 std :: cout << std :: endl ;
24 return ( EXIT_SUCCESS );
25 }
Rezultat działania:
1 HAU ! HAU ! HAU ! MIAU ! HAUuUu ! HAUuUu ! HAUuUu !
11/14
Obiektowe programowanie w języku C++
Dziedziczenie - różnica między wiązaniem statycznym i dynamicznym
Poniższe przykłady ilustrują różnice między wiązaniem statycznym i dynamicznym.
Statyczne Dynamiczne
1 # include < iostream > 1 # include < iostream >
2 using namespace std ; 2 using namespace std ;
3 class A { 3 class A {
4 public : 4 public :
5 void f () { 5 virtual void f () {
6 cout << " Klasa A" << endl ; } 6 cout << " Klasa A" << endl ; }
7 }; 7 };
8 class B : A { 8 class B : A {
9 void f () { 9 void f () {
10 cout << " Klasa B" << endl ; } 10 cout << " Klasa B" << endl ; }
11 }; 11 };
12 void g(A& arg ) { 12 void g(A& arg ) {
13 arg .f (); 13 arg .f ();
14 } 14 }
15 int main () { 15 int main () {
16 B x; 16 B x;
17 g(x ); 17 g(x );
18 } 18 }
1 Rezultat wykonania : Klasa A 1 Rezultat wykonania : Klasa B
Gdy funkcja g jest wywoływana, wywołuje się Typ aktualnego obiektu może być określony
funkcja A::f, mimo że argument wskazuje na w trakcie wykonywania programu. Słowo vir-
obiekt typu B. W fazie kompilacji, kompilator tual określa, że kompilator powinien wybrać
wie tylko, że argument funkcji g jest referencją odpowiedni aspekt funkcji f nie na podstawie
do obiektu typu pochodnego od A; nie może zaś typu referencji, ale na podstawie typu obiektu
określić typu rzeczywistego obiektu (A lub B). identyfikowanego przez tę referencję.
12/14
Obiektowe programowanie w języku C++
Klasy abstrakcyjne
Służą one do zdefiniowania tzw. interfejsu, który będzie użyty we wszystkich klasach
pochodnych wywodzących się z danej klasy bazowej.
Interfejs
Interfejs oznacza zbiór publicznie dostępnych metod danej klasy, za pomocą których
można z poziomu innych obiektów lub z ciała funkcji globalnych wywoływać operacje
zdefiniowane w klasie przez jej metody.
Klasę abstrakcyjną w języku C++ definiuje się w sposób pośredni, poprzez
zadeklarowanie co najmniej jednej funkcji składowej jako czysto wirtualnej, np.:
virtual void fcw() = 0;
Funkcja taka nie ma w danej klasie ciała, a więc nie jest znany jej kod.
Dlatego nie można:
utworzyć obiektu o typie zgodnym z typem klasy abstrakcyjnej (nie ma ona tej
części funkcjonalności, jaka jest kojarzona z jej czysto wirtualnymi funkcjami)
użyć klasy abstrakcyjnej jako typu parametru funkcji
użyć klasy abstrakcyjnej jako typu zwracanej wartości
użyć klasy abstrakcyjnej jako typu docelowego w przekształceniu typu
(rzutowaniu).
13/14
Obiektowe programowanie w języku C++
Klasy abstrakcyjne
Można jednak:
deklarować wskazniki i referencje do obiektów klasy abstrakcyjnej.
użyć klas abstrakcyjnych jako nadklasy dla klas konkretnych. W trakcie
dziedziczenia podklasy muszą zdefiniować wszystkie funkcje czysto wirtualne;
dopiero w momencie zdefiniowania wszystkich f. czysto wirtualnych uzyskuje się
klasę konkretną, dla której można utworzyć obiekt/y.
Przykład
System operacyjny powinien udostępniać w ograniczony sposób informacje o swoich
sterownikach. W tym celu można zastosować klasę abstrakcyjną, która tylko definiuje
interfejs do wszystkich sterowników, tym samym wymuszając ich zgodność:
1 class Device {
2 public :
3 virtual int open () = 0;
4 virtual int close () = 0;
5 virtual int read ( const char *) = 0;
6 virtual int write ( const char *) = 0;
7 virtual int ioctl ( int , ...) = 0;
8 // ...
9 };
14/14
Obiektowe programowanie w języku C++
Zawieranie obiektów (relacja całość-część)
Relacja całość-część określa zawieranie się jednych obiektów w innych. Na schematach
struktury obiektowej stosuje się pokazane niżej oznaczenia, przy czym napisy  0-1 i
 0..n oznaczają, że klasa Pojemnik, o ile istnieje jej obiekt, zawiera 0 lub więcej
obiektów klasy Element jako tzw. pole obiektowe (obiekt zawarty w innym obiekcie).
15/14
Obiektowe programowanie w języku C++
Reagowanie na sytuacje wyjątkowe
Nawet najlepiej zaprojektowany program zwykle posiada pewną liczbę (nieznaną)
błędów, wynikających z błędnego modelu przetwarzania i z błędnej implementacji
dobrego modelu. Ponadto program jest narażony na błędy obsługi i danych
wejściowych.
W celu zapewnienia odpowiedniego poziomu niezawodności oprogramowania, stosuje
się dwa główne podejścia:
tradycyjne, mające na celu zabezpieczenie się przed powstaniem sytuacji
wyjątkowych, w rodzaju dzielenia przez zero, dostępu do elementu tablicy o
niewłaściwym indeksie lub użycia niezainicjowanego wskaznika. W tej metodzie
bada się wartości odpowiednich zmiennych lub wyrażeń przed wykonaniem
krytycznych operacji. Jest to jednak zawodna i uciążliwa technika, ponieważ
wymaga ona przewidywania wszystkich anormalnych sytuacji, a ponadto generuje
ciąg instrukcji sprawdzających niezależnie od rzeczywistej potrzeby  na wszelki
wypadek.
oparte o mechanizm obsługi wyjątku. Lepiej i na ogół prościej jest zlecić
wykonanie operacji, a dopiero w razie zaistnienia sytuacji wyjątkowej
odpowiednio na nią zareagować. Ten ostatni problem nie jest prosty i nie ma
ogólnego rozwiązania  często nie wiadomo co zrobić w określonych sytuacjach
awaryjnych i czy można kontynuować wykonywanie się programu  wtedy lepiej
zapisać dane i zakończyć program.
16/14
Obiektowe programowanie w języku C++
Reagowanie na sytuacje wyjątkowe
Kategorie sytuacji wyjątkowych
Wyróżnia się następujące kategorie sytuacji wyjątkowych:
tzw. systemowe, generowane przez system operacyjny i/lub sprzęt. Należą do
nich wyjątki dzielenia przez zero, niedomiaru lub nadmiaru zmiennopozycyjnego,
dostępu do pamięci, itp. W ramach każdego systemu operacyjnego jest dokładnie
określona lista generowanych (a zatem i obsługiwanych) wyjątków tego rodzaju.
Np. w systemie Windows NT zaimplementowano system obsługi tej kategorii
wyjątków pod nazwą Structured Exception Handling.
programowe, o dowolnej potrzebnej kategorii. Kategoria ta zależy od
przewidywanej przez programistę niesprawności kodu, np. nieodpowiednia ilość
odczytanych danych, niespełnienie istotnego warunku integralności danych, brak
danej w skończonym czasie  timeout, itp.
Wymienione kategorie wymagają odmiennej obsługi na poziomie kodu zródłowego.
Jednak w obu wymienionych przypadkach należy zadbać o obsługę wszystkich
możliwych do wystąpienia wyjątków, w przeciwnym razie normalną akcją
oprogramowania systemowego jest awaryjne zakończenie wykonania programu,
połączone z utratą nie zapisanych danych. Jednakowy jest za to mechanizm działania
systemowych procedur obsługi wyjątku: zostaje przywrócony stan stosu programu,
jaki istniał w momencie zgłoszenia wyjątku (tzw. stack unwinding), co oznacza
usunięcie wszystkich zmiennych lokalnych utworzonych w funkcjach wywołanych od
tego momentu (w tym obiektów, przy czym wykonują się ich destruktory).
17/14
Obiektowe programowanie w języku C++
Reagowanie na sytuacje wyjątkowe
Obsługa wyjątków programowych
Osiąga się to przez zastosowanie tzw. instrukcji nadzorującej o postaci:
1 try {
2
3 }
4 < Handler1 >
5 < Handler2 >
6 ....
7 < HandlerN >
gdzie Body jest ciałem instrukcji złożonej, a HandlerX jest definicją nienazwanej
funkcji obsługi przewidzianej do wywołania w razie wystąpienia wyjątku.
Postać funkcji obsługi:
1 catch ( Par ) {
2 Body
3 }
Parametr Par jest deklarowany podobnie jak zwykły parametr funkcji, z podaniem
typu i nazwy (to drugie jest opcjonalne  konieczne wtedy, gdy wewnątrz funkcji
obsługi używa się tego parametru).
Jeśli nie wystąpią wyjątki, to wykonanie instrukcji Body polega na wykonaniu jej ciała
i zignorowaniu wszystkich następujących po niej funkcji obsługi wyjątków, nie ma więc
żadnych dodatkowych obciążeń związanych z istnieniem kodu nadzorującego.
18/14
Obiektowe programowanie w języku C++
Reagowanie na sytuacje wyjątkowe
Natomiast jeśli w instrukcji nadzorującej zostanie wysłany wyjątek, co jest realizowane
przez wykonanie instrukcji:
throw Exp; // operacja wyslania wyjatku
gdzie Exp jest pewnym wyrażeniem, to sterowanie jest przekazane do tej funkcji
obsługi, której specyfikacja zgadza się z typem wyrażenia Exp. Po wykonaniu
instrukcji zawartych w funkcji obsługi, nie jest kontynuowane wykonywanie się
instrukcji nadzorującej.
UWAGA: Jeśli wyjątek zostanie wysłany za pomocą throw, a nie dostarczono żadnej
lub odpowiedniej funkcji obsługi, to wywoływana jest funkcja specjalna terminate(),
która powoduje zakończenie programu.
Specyfikowanie wyjątków dla funkcji
Deklaracja każdej funkcji może być uzupełniona o specyfikację wyjątków, jakie może
ona wygenerować. Specyfikacja ta ma postać:
throw ( Lista )
gdzie Lista jest listą oznaczeń dopuszczalnych wyjątków, np.:
void fun(void) throw (complex, int );
Deklaracja ta ma następujące skutki:
Podana funkcja może wygenerować co najwyżej wyjątki podanych typów.
Jeśli w trakcie wykonywania tej funkcji wysłany byłby wyjątek typu nie zawartego
w liście, to wywołana będzie specjalna funkcja unexpected(), która typowo
powoduje zakończenie programu.
Jeśli lista dopuszczalnych wyjątków jest pusta (występuje samo słowo throw), to
funkcja nie może generować żadnych wyjątków.
19/14
Obiektowe programowanie w języku C++
Reagowanie na sytuacje wyjątkowe
Przykład
Funkcja operatorowa [] (czyli operator indeksowania), która może być zdefiniowana dla
klasy Tablica, w celu zapewnienia dostępu do elementów przechowywanych w
prywatnym polu tab typu tablicowego. Pozwoli to zrealizować odczyt lub zapis
wartości. Funkcja ta generuje w pewnych sytuacjach wyjątek typu int, który zawiera
wartość indeksu wykraczającą poza zakres tablicy.
1 T& operator []( int i) {
2 if (i < 0 || i >= I)
3 throw i; // wyslanie wyjatku
4 return tab [i ];
5 }
Wywołując tą funkcję, należy użyć kodu przedstawionego poniżej:
1 int main ( int argc , char * argv []) {
2 Tablica < char , 10 > w;
3 ind = 10;
4 try {
5 w[ ind ] =  A  ; // proba dostepu
6 }
7 catch ( int i) { // obsluga wyjatku typu int
8 cerr << " Zly indeks tablicy : " << i << endl ;
9 return -1;
10 }
11 catch (...) { // wyjatek innego , nieznanego typu
12 cerr << " Nieznany wyjatek !";
13 return -1;
14 }
15 return 0;
16 }
20/14


Wyszukiwarka

Podobne podstrony:
Wyklad CPP 6
Wyklad CPP 1
Wyklad CPP 2
Wyklad CPP 7
Wyklad CPP 5
CPP WYKLAD 1
CPP WYKLAD 7
CPP WYKLAD 6
CPP WYKLADY ANALIZA 2
CPP WYKLAD 1 2
CPP WYKLAD 3
CPP WYKLAD 4 5
CPP WYKLADY ANALIZA 1
Sieci komputerowe wyklady dr Furtak
Wykład 05 Opadanie i fluidyzacja

więcej podobnych podstron