Professional Documents
Culture Documents
Zadanie
Zdefiniować klasę Kolo dla kół, klasę Tekst dla tekstów oraz klasę Okno dla kól z tekstem.
Klasa Okno musi dziedziczyć od dwóch rodziców: Kolo i Tekst.
W każdej klasie muszą być konstruktory z argumentami, a implementacja konstruktora w klasie
Okno musi być z listą inicjacyjnej.
Do każdej klasy dodać metodę „void Rysuj(TPaintBox* pb,int x0, int y0);”
(dla klasy Tekst interpretować „Rysuj” jako „Pisz”).
Dodać do formularza graficzne obiekty w celu wprowadzenia od użytkownika pozycji (x0,y0)
obiektu klasy Okno. Po naciśnięciu klawiszu „Start” w graficznej części programu wyświetlać koło z
tekstem testowym na wskazanej pozycji.
======================================================
„Wyjątki a klasy”
Tematy ćwiczenia
• obsługa wyjątków
Plan
a)
W procesie wykonania programu mogą być sytuacje wyjątkowe (wyjątki) spowodowane nie tyle
błędami programisty, ile wprowadzeniem niepoprawnych danych przez użytkownika, na przykład nazwy
pliku nieistniejącego, wprowadzeniem liter zamiast cyfr itp.
Z reguły sytuacja wyjątkowa kończy się awaryjnym zakończeniem programu. Oprócz stresu
powstającego u użytkownika, awaryjne zakończenie powoduje „zanieczyszczenie” pamięci obiektami
dynamicznymi, a dysku komputera - plikami tymczasowymi.
Ponieważ sytuacje wyjątkowe powstają często, kompilator dodaje do programu niewidzialne dla
programisty procedury do obsługi często powstających wyjątków.
W momencie powstania wyjątku te procedury informują użytkownika standardowymi komunikatami
o sytuacji wyjątkowej. I tutaj powstaje problem polegający na tym, że informacja o sytuacji wyjątkowej
wyświetla się w języku autorów kompilatora, tj. zwykle w języku angielskim. Oprócz tego komunikaty
standardowe są zwykle krótkie i nie zawsze jasne dla użytkownika.
Aby poinformować użytkownika o sytuacji wyjątkowej w języku narodowym i komunikatem jasnym
dla użytkownika, programista musi dodać do programu obsługę wyjątków.
Zasady obsługi wyjątków trochę różnią się w standardowym języku C++, w środowisku C++
Builder, w języku Microsoft C++ i w bibliotece MFC.
Blok try-catch
W standardowym języku C++ żeby obsłużyć wyjątek, należy zbudować w tekście programu tzw.
blok try-catch.
Blok try-catch ma składnię następującą:
try
{
// instrukcje, przy których wywołaniu
// może mieć miejsce wyjątek
}
catch (Inf_o_wyjątku)
{
// instrukcje obsługi wyjątku
}
W przypadku powstania sytuacji wyjątkowej wewnątrz bloku try program buduje (alokuje) obiekt
typu Klasa_wyjątku i wykonuje instrukcji bloku catch.
W nagłówku bloku catch (w miejscu (Inf_o_wyjątku)) może być zapisane:
- (Nazwa_typu_wyjątku& Parametr_formalny) lub (Nazwa_typu_wyjątku
Parametr_formalny), lub (Nazwa_typu_wyjątku* pParametr_formalny) w przypadku
wykorzystania w instrukcjach bloku catch parametru formalnego Parametr_formalny,
- (Nazwa_typu_wyjątku&) lub (Nazwa_typu_wyjątku), jeśli parametr formalny nie jest
potrzebny,
- (...) (tj. 3 kropki) w przypadku wykonania instrukcji bloku catch niezależnie od typu wyjątku.
Blok catch nie jest podprogramem, chociaż wygląda jak podprogram z argumentem.
Może być nie jeden blok catch, a tyle, ile klas wyjątków chcemy obsłużyć. Wtedy bloki powinny
być zapisane jeden po drugim.
Blok catch z nagłówkiem (...) musi być ostatni w grupie bloków catch.
Kompilator dodaje do programu takie instrukcje, żeby w chwilę powstania wyjątku zaalokować
obiekt typu Typ_wyjątku i przekazać ten obiekt bloku catch.
Konstrukcja throw
Wywołanie instrukcji bloku catch z komunikatem o wyjątku i innymi operacjami (zamykanie plików
itp.) można spowodować konstrukcją ze słowem kluczowym throw. Konstrukcję throw trzeba
umieszczać w tym miejscu programu, gdzie sprawdzamy sytuację wyjątkową.
Poza słowem kluczowym throw może znajdować się wywołanie konstruktora obiektu klasy
Klasa_wyjątku lub wartość.
Klasę Klasa_wyjątku może zdefiniować programista. Przez obiekt wyjątku można przekazać
bloku catch aktualne wartości zmiennych z bloku try.
Przykład:
try
{
if (...) throw „Nie wprowadzone jest hasło”;
else {...}
}
catch (const char* pt) // typ wyjątku „const char*”
{
Application->MessageBox(pt,"Błędy",MB_OK);
}
Liczba całkowita napisana za słowem kluczowym throw powoduje typ wyjątku int. Tą liczbę
można wykorzystać jako numer sytuacji wyjątkowej podobne jak w następującym tekście:
try
{
if (...) throw 1;
else
if (...) throw 2;
else
...;
}
catch (int& numer) // typ wyjątku „int”
{
switch (numer)
{
case 1:
...
break;
case 2:
...
break;
}
}
W konstrukcji ze słowem throw można wskazywać obiekt z typem zdefiniowanym wcześniej.
Łączenie z blokiem catch przez obiekt z typem zdefiniowanym wcześniej daje dodatkową możliwość
wielokrotnego wykorzystywania jednego bloku catch. Na przykład:
typedef struct
{
int ii; char ch[16];
} s_s;
s_s ob1, ob2;
...
try
{
if (...) throw ob1;
else
if (...) throw ob2;
else
...;
}
catch (s_s& ob) // typ wyjątku „s_s”
{
char temp[64];
sprintf(temp,”Stan obiektu: ii=%d, ch=\”%s\””,ob.ii,ob.ch);
Application->MessageBox(temp,"Błędy",MB_OK);
}
int bok=Edit1->Text.ToInt();
Wprowadzenie boku kwadratu większego niż szerokość lub wysokość obiektu graficznego typu
TPaintBox będziemy rozpatrywać jako sytuację wyjątkową.
Żeby otrzymać w postaci łańcucha komunikatów w jednej aplikacji komunikaty dla każdego ze
wskazanych wariantów, zbudować hierarchie bloków try-catch:
Uwaga. Przy sprawdzaniu pracy programu uruchamiać aplikację poza środowiskiem Builder’a,
żeby nie widzieć dodatkowych komunikatów debugger’a środowiska o typie wyjątku.
b)
Ograniczenie obsługiwania wyjątku
Obsługiwanie wyjątku można ograniczyć przez blokowanie bloku catch. Blokowanie może być
potrzebne na przykład, jeżeli w tekście programu jest wywoływana standardowa funkcja, która produkuje
komunikaty o sytuacji wyjątkowej, a autor programu nie chce pokazywać te komunikaty użytkownikowi.
W celu ograniczenia obsługiwania wyjątku należy na końcu definicji funkcji (podprogramu, metody)
dodać specyfikację wyjątku (ang. exception specification).
Jasne, że w głównym tekście należy wynieść instrukcji lub wywołania funkcji do podprogramu, a
specyfikację wyjątku dodać na końcu definicji nowej funkcji (podprogramu, metody).
Specyfikacja ograniczenia wyjątku ma następującą składnię:
class exc
{
public:
int aa;
exc(int arg){aa=arg;};
};
double Dzielen(int d) throw()
{
double ret=0;
if (d==0) throw exc(5);
else ret=1/d;
return ret;
}
void __fastcall TForm1::BitBtnStartClick(TObject *Sender)
{
try
{
Dzielen(0);
}
catch (exc& par)
{
// tekst komunikatu:
char temp[128];
sprintf(temp,"Test %d",par.aa);
Application->MessageBox(temp, "Komunikat", MB_OK);
}
}
Zadanie. Wynieść porównanie boku kwadratu z szerokością i wysokością obiektu graficznego typu
TPaintBox w podprogram.
Uruchomić program z wyprodukowaniem sytuacji wyjątkowej.
W celu ograniczenia na obsługiwanie wyjątków na końcu definicji podprogramu dodać specyfikację
wyjątku „throw()”.
Uruchomić program z wyprodukowaniem sytuacji wyjątkowej w nowym wariancie i w celu
zaliczenia punktu pokazać prowadzącemu pracę programu i odpowiedzieć na pytanie, co uległo zmianie.
c)
Środowisko C++ Builder zawiera obsługę wyjątków standardowego języka C++ i rozszerza
obsługę wyjątków na obsługę wyjątków strukturalnych oraz wyjątków biblioteki VCL.
Jeżeli na poziomie bloku try nie ma bloku catch z odpowiednim typem wyjątku, to
odpowiedniego bloku catch szuka się na zewnątrz.
Jeżeli odpowiedni blok catch nie będzie znaleziony, to wyjątek będzie obsługiwać metoda
TApplication::HandleException. Metoda wyświetla zwykle komunikat standardowy i tylko dla
wyjątków klasy EAbort nic nie wyświetla.
Metodę TApplication::HandleException można zamienić na inną. W tym celu w klasie
TApplication jest atrybut OnException jako wskaźnik na funkcje (procedurę). Można napisać swoją
procedurę, na przykład pod nazwą fMyProcExcept:
oraz na początku programu (w konstruktorze klasy TForm1 lub w procedurze OnCreate) dodać
instrukcję następującą:
Application->OnException=fMyProcExcept;
d)
Klasa Exception
Klasą bazową dla obsługi wyjątków w środowisku C++ Builder jest klasa Exception, która
bezpośrednio dziedziczy od głównej klasy TObject.
Definicja klasy Exception rozmieszcza się w pliku SysUtils.hpp. W klasie są zdefiniowane
dwa atrybuty i wiele konstruktorów.
Atrybutami (polami danych) klasy Exception są:
HelpContext typu int do zapamiętywania identyfikatora okna pomocy. Okno pomocy wyświetla
się, jeżeli nacisnąć klawisze F1 w czasie wyświetlania okna komunikatu o sytuacji wyjątkowej.
Przygotowany wcześniej plik pomocy musi być dołączony do projektu programu przez opcję Help file
na stronie Application, do której można dostać się przez menu Project/Options.
- Message typu AnsiString do zapamiętywania tekstu komunikatu o sytuacji wyjątkowej.
Konstruktory klasy Exception
Klasa Exception zawiera wiele konstruktorów, z których większość może być przesłonięta w
klasach pochodnych.
Konstruktorami klasy Exception są następujące konstruktory:
- Exception(const AnsiString Msg), który zapisuje wiersz komunikatu Msg do atrybutu
Message,
- Exception(const AnsiString Msg, const System::TVarRec * Args, const int
Args_Size), który zapisuje do atrybutu Message wiersz formowany na bazie opisującego format
wiersza Msg i tablicy argumentów Args rozmiarem Args_Size,
- Exception(int Ident), który zapisuje do atrybutu Message wiersz z identyfikatorem Ident w
rozdziale resursów,
- Exception(System::PResStringRec ResStringRec), który zapisuje wiersz systemowego
komunikatu ResStringRec do atrybutu Message,
- Exception(int Ident, const System::TVarRec * Args, const int Args_Size),
który zapisuje do atrybutu Message wiersz formowany na bazie opisującego format wiersza z
identyfikatorem Ident w rozdziale resursów i tablicy argumentów Args rozmiarem Args_Size,
- Exception(System::PResStringRec ResStringRec, const System::TVarRec *
Args, const int Args_Size), który zapisuje do atrybutu Message wiersz formowany na bazie
opisującego format wiersza ResStringRec i tablicy argumentów Args rozmiarem Args_Size,
- Exception(const AnsiString Msg, int AHelpContext), który zapisuje wiersz komunikatu
Msg do atrybutu Message oraz numer strony AHelpContext w pliku pomocy do atrybutu
HelpContext,
- Exception(const AnsiString Msg, const System::TVarRec * Args, const int
Args_Size, int AHelpContext), który zapisuje do atrybutu Message wiersz formowany na
bazie opisującego format wiersza Msg i tablicy argumentów Args rozmiarem Args_Size oraz
numer strony AHelpContext w pliku pomocy do atrybutu HelpContext,
- Exception(int Ident, int AHelpContext), który zapisuje do atrybutu Message wiersz z
identyfikatorem Ident w rozdziale resursów oraz numer strony AHelpContext w pliku pomocy do
atrybutu HelpContext,
- Exception(System::PResStringRec ResStringRec, int AHelpContext), który zapisuje
wiersz systemowego komunikatu ResStringRec do atrybutu Message oraz numer strony
AHelpContext w pliku pomocy do atrybutu HelpContext,
- Exception(System::PResStringRec ResStringRec, const System::TVarRec *
Args, const int Args_Size, int AHelpContext), który zapisuje do atrybutu Message
wiersz formowany na bazie opisującego format wiersza ResStringRec i tablicy argumentów Args
rozmiarem Args_Size oraz numer strony AHelpContext w pliku pomocy do atrybutu
HelpContext,
- Exception(int Ident, const System::TVarRec * Args, const int Args_Size,
int AHelpContext), który zapisuje do atrybutu Message wiersz formowany na bazie opisującego
format wiersza z identyfikatorem Ident w rozdziale resursów i tablicy argumentów Args rozmiarem
Args_Size oraz numer strony AHelpContext w pliku pomocy do atrybutu HelpContext.
Konstrukcja throw
Konstruktory klasy Exception i klas pochodnych od klasy Exception należy wykorzystywać w
konstrukcji ze słowem kluczowym throw:
throw Klasa_wyjątku(Parametry_konstruktora);
Podobnie do standardowego języka C++ konstrukcję throw trzeba umieszczać w tym miejscu
programu, gdzie sprawdzamy sytuację wyjątkową.
Przykład zapisywania konstrukcji throw: