You are on page 1of 9

Zadanie sprawdzające w środowisku Borland C++ Builder na temat

„Dziedziczenie od wielu rodziców ( wielo bazowe)”

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.

Pokazać prowadzącemu pracę programu w celu zaliczenia.

======================================================

Ćwiczenie laboratoryjne w środowisku Borland C++ Builder na temat

„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.

Obsługa wyjątków w standardowym języku C++

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:

class exc //klasa opisująca obiekt wyjątku


{
public:
int aa;
exc(int arg){aa=arg;};
};
double Dzielen(int d)
{
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);
}
}
W przypadku, kiedy poza słowem kluczowym throw znajduje się wartość, ona jest przekazywana
bloku catch przez obiekt tego typu, który odpowiada tej wartości.
Jeżeli za słowem kluczowym throw będzie napisany wiersz, to kompilator interpretuje typ wyjątku
jako const char*.
Na przykład, można komunikować o sytuacji wyjątkowej następująco:

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);
}

Za pomocą słowa kluczowego throw można spowodować powtórną generację wyjątku.


Powtórną generację wyjątku może być potrzebna w związku z tym, że na zakończenie obsługi
wyjątku w bloku catch obiekt wyjątku ulega niszczeniu. Powtórna generacja wyjątku jest niezbędna,
jeżeli chcemy wykonać instrukcje „awaryjne” w zewnętrznych procedurach (blokach), lub wyświetlić
łańcuch komunikatów od włożonych procedur lub włożonych bloków.
Schemat dwuetapowej generacji wyjątku wygląda następująco:

try // blok zewnętrzny


{
...
try //blok wewnętrzny
{
... // instrukcje generujące wyjątek typu „exc1”
}
catch (exc1&)
{
... // obsługa wyjątku typu „exc1”
throw; // powtórna generacja wyjątku
}
...
}
catch (exc1 &)
{
... // przedłużenie obsługi wyjątku typu „exc1”

W standardowym języku C++ do obsługi wyjątków są przygotowane bazowa klasa exception


oraz klasy dziedziczone od niej.
Klasa exception jest opisana w pliku exceptio.h. Klasa exception ma metodę operatorową
operator= oraz wirtualną metodę what(). Za pomocą metodę what() można otrzymać wiersz z
tekstem komunikatu.
W przykładzie jest przedstawiona pewna procedura Dzielen z operacją dzielenia, dla której z
wysokim prawdopodobieństwem może powstać wyjątek. W przykładzie w przypadku powstania wyjątku
będzie zaalokowany obiekt standardowej klasy exception.
Przykład. Procedura w języku C++ z blokiem try-catch.
#include <exceptio.h>
using namespace std;
...
double Dzielen(int d)
{
double ret=0;
try
{
if (d==0) throw exception();
else
ret=1/d;
}
catch (exception& ob)
{
// tekst komunikatu standardowej klasy exception:
Application->MessageBox(ob.what(),"Komunikat",MB_OK);
// tekst „swojego” komunikatu:
Application->MessageBox("Nie można wprowadzać zero","Komunikat",MB_OK);
}
return ret;
}

Zadanie. W jednostce („unit”) zdefiniować klasę Kwadrat, a do głównego formularzu dodać


graficzny obiekt typu TEdit, żeby mieć możliwość wprowadzenia rozmiaru boku kwadratu, oraz dodać
obiekt graficzny typu TPaintBox do wyświetlenia kwadratu.
Do przekształcenia wprowadzonego rozmiaru boku z postaci tekstowej do liczby można
wykorzystać instrukcję:

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ą.

Obsłużyć sytuację wyjątkową:


1) przez konstrukcję „throw „tekst””,
2) przez konstruktor z argumentem obiektu klasy „klObsl” (zadać typ argumentu konstruktora
„const char* pstr”),
3) przez obiekt klasy „klObsl”,
4) przez klasę exception.

Ż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:

try // blok zewnętrzny


{
..
try //blok wewnętrzny
{
..
try //blok wewnętrzny
{
..
try //blok wewnętrzny
{
..
if (..) throw exc1();
}
catch (exc1& ob1)
{
.. // obsługa wyjątku typu „exc1”
throw exc2; // powtórna generacja wyjątku
}
}
catch (exc2& ob2)
{
.. // obsługa wyjątku typu „exc2”
throw exc3; // powtórna generacja wyjątku
}
}
catch (exc3& ob3)
{
.. // obsługa wyjątku typu „exc3”
throw exc4; // powtórna generacja wyjątku
}
}
catch (exc4& ob4)
{
... // obsługa wyjątku typu „exc4”
}

Uwaga. Przy sprawdzaniu pracy programu uruchamiać aplikację poza środowiskiem Builder’a,
żeby nie widzieć dodatkowych komunikatów debugger’a środowiska o typie wyjątku.

W celu zaliczenia punktu pokazać prowadzącemu pracę programu.

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ę:

throw() lub throw(typ) lub throw(typ1, typ2, ..)

Konstrukcja throw() wyeliminuje obsługiwanie wszystkich wyjątków.


W innych konstrukcjach zezwolono obsługiwanie wyjątków tylko wskazanych typów.
Jeżeli na końcu definicji podprogramu (funkcji, metody) nic nie jest dodane (a jest to definicją
typową), to nie ma ograniczeń na obsługiwania wyjątków.
Przykład z ograniczeniem obsługiwania wyjątków:

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:

void __fastcall TForm1::fMyProcExcept(TObject* Sender, Exception* pE)

oraz na początku programu (w konstruktorze klasy TForm1 lub w procedurze OnCreate) dodać
instrukcję następującą:

Application->OnException=fMyProcExcept;

Zadanie. Zlikwidować ograniczenie na obsługiwanie wyjątków i powrócić na tekst programu z


obsługą wyjątku.
Zablokować jeden z bloków catch oraz odpowiadający mu wiersz ze słowem try, żeby zostawić w
tekście konstrukcję throw z typem wyjątku zablokowanego bloku catch. Tym samym będzie imitowana
sytuacja, kiedy nie ma odpowiedniego bloku catch i aplikacja będzie wywoływać metodę
TApplication::HandleException.
Popatrzyć na komunikat standardowy.
Napisać swoją procedurę obsługiwania wyjątku nie obsługiwanego w tekście programu i podmienić
metodę TApplication::HandleException.

Uruchomić program z wyprodukowaniem sytuacji wyjątkowej w nowym wariancie i w celu


zaliczenia punktu pokazać prowadzącemu pracę programu.

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:

throw Exception(AnsiString(„Mało danych”),2);

gdzie liczba 2 oznacza numer strony tekstu pomocy.


Zadanie.
Obsłużyć sytuacją wyjątkową przez klasę Exception korzystając z konstruktora z jednym
argumentem - tekstem komunikatu.

W celu zaliczenia punktu pokazać prowadzącemu prace programu.

Zapisać projekt w trybie „File / Save”.


Zamknąć program Borland C++ Builder.

You might also like