Zaawansowane szukanie | Nowości
Newsy | Artykuły | Programowanie: Pascal, Object/Delphi Pascal, C++, PHP, Javascript | Poradniki | Recenzje | FAQ | Mapa

Artykuł: C++ - Pokonywanie błędów

Programista idealny?


Jak wiadomo nikt nie jest idealny, każdy programista popełnia błędy, dlatego trzeba uważnie pisać każdy kawałek kodu, zwracać uwagę nawet na to, że nie wszyscy będą chcieli używać programu zgodnie z naszym zaleceniem. Warto się zabezpieczać. W tym artykule opiszę jakie błędy są najcześciej popełniane przez programistów oraz jak zabezpieczać swoje programy.

Najczęstrze błędy


Nie da się skompilować programu, który jest błednie napisany. Dlatego gdy popełniamy błędy w kodzie, C++ Builder wskazuje je nam i podaje wskazówkę, jak je naprawić. Najprostrzym przykładem jest zastosowanie funkcji ShowMessage:
 ShowMesage("Treść komunikatu");
Teraz przy próbie kompilacji programu zobaczymy coś takiego:

Wskazywanie błędów przez C++ Buildera


C++ Builder ma wbudowany Debuger, dzięki któremu pokazuje nam m.in. błędy w aplikacji. Nie dość, że kursor wskaże nam miejsce występowania błędu, to jeszcze zostaniemy przeniesieni do danej linii, zostanie ona zaznaczona na czerwono, a pod oknem kodu pokaże się okienko ze wskazówką czego bład dotyczy. W tym przypadku wskazówka mówi nam "Call to undefined function 'ShowMesage'". Jest to często spotykany błąd, ponieważ zabrakło jednej literki "s":
ShowMessage("Treść komunikatu");
Teraz już wszystko działa jak powinno. Drugim często spotykanym błędem w funkcjach jest pisanie wszystkiego z małych liter, np.:
 int zmienna;
 zmienna = strtoint(Edit1->Text);
Komunikat jest taki sam - nie ma takiej funkcji. Języki takie jak np. ObjectPascal nie zwracają uwagi na wielkość liter, jednak w C++ trzeba sie tego pilnować.
Nie sposób mi przedstawić wszystkich rodzajów takich błędów - są nimi jeszcze np. brak średnika na końcu procedury - takich rzeczy trzeba się pilnować.

Złośliwi użytkownicy - czyli program idiotoodporny


Przedstawię teraz jak mogą wpłynąć na nasz program złośliwi użytkownicy i jak się przed nimi zabezpieczyć. Otwórzmy teraz program z naszą pętlą, gdzie wyświetlało się 30 pozycji. Dodajmy funkcję, która po wybraniu elementu wyświetli nam go z listy. Usuńmy pętlę i dodamy 5 pozycji na listę (typu "pozycja1", "pozycja2" itd.). Dzięki poniższej procedurze uda się to zrobić:
 int zmienna;
 zmienna = lista->ItemIndex + 1;
 ShowMessage("pozycja" + IntToStr(zmienna));
 
Jeżeli umieścimy tą procedurę do zdarzenia OnClick dla naszego przycisku, wyświetli on element z naszej listy (w tym przypadku "pozycja" + jej numer). Jedną rzecz muszę wyjaśnić: elementy na liście są numerowane inaczej, tzn. pierwsza pozycja ma numer 0, a każda następna o 1 więcej, więc pozycja druga będzie miała numer 1. Aby wyświetlało nam pozycję taką, jaka powinna być, dodałem do zmiennej "zmienna" 1. Dzięki ItemIndex można wyciągnąć dany numer aktualnie zaznaczonej pozycji na liście. Przejdę do rzeczy - program ma wyświetlać pozycje i to tylko te z listy - jeśli nie zaznaczymy żadnej pozycji, program zwróci nam komunikat "pozycja0". Jest to jednak niezgodne z działaniem programu, ponieważ na liście nie ma "pozycja0".
 int zmienna;
 zmienna = lista->ItemIndex + 1;
 if (zmienna == 0) {
    ShowMessage("Wybierz pozycję z listy!");
 }
 else {
      ShowMessage("pozycja" + IntToStr(zmienna));
 }
Jak widać jeżeli zmienna wyniesie 0, wyświetli się komunikat "Wybierz pozycję z listy!". Gdyby zmienna nie dodawała 1 do indexu, program zwróciłby pozycję -1, gdyby nic nie zostało zaznaczone. Teraz program działa jak powinien.
Stwórzmy teraz nowy program. Potrzebne nam będą komponenty takie jak Edit (o nazwie "pole"), Button (o nazwie "OK") oraz Label (z tekstem "Podaj liczbę od 1 do 5"). Całość może wyglądać tak:
Forma aplikacji
Teraz sama procedura, która wyświetli nam liczbę wpisaną w pole tekstowe:
void __fastcall Tforma::przyciskOKClick(TObject *Sender)
{
 int zmienna;
 zmienna = StrToInt(pole->Text);
 ShowMessage(zmienna);
}
Wszystko niby działa pięknie, ale jak ktoś poda liczbę 7, -3, 0? Oczywiście zostanie ona wyświetlona, ale nie tak ma działać program. Oto poprawna procedura:
void __fastcall Tforma::przyciskOKClick(TObject *Sender)
{
 int zmienna;
 zmienna = StrToInt(pole->Text);
 if (zmienna <= 0) {
 	ShowMessage("Podana liczba jest mniejsza lub równa od 0. Podaj liczbę z przedziału 1-5");
 }
 else if (zmienna > 5) {
 	ShowMessage("Podana liczba jest większa od 5. Podaj liczbę z przedziału 1-5");
 }
 else {
 	ShowMessage("Podana liczba z przedziału 1-5 to: " + IntToStr(zmienna));
 }
}
Dzięki temu warunkowi wszystko będzie działać poprawnie. Czas poprawić nasz "Ala Kalkulator". Ulepszoną wersję nazwijmy 2. Przy okazji zmieniłem jej układ i nazwę obiektu ComboBox na "dzialanie". Wszystko wygląda tak:
Ala Kalkulator 2
Pierwsze co rzuca się w oczy to brak przyciska OK, ale można sobie poradzić bez niego. Najlepiej zrobić tak, aby wraz ze zmianą działania obliczał się wynik. Zaznaczamy więc naszą listę rozwijalną "dzialanie" i wybieramy z zakładki zdarzeń (Events) w Inspektorze Obiektów akcję OnChange:
Akcja OnChange
Czas napisać procedurę:
void __fastcall TForm1::dzialanieChange(TObject *Sender)
{
int pierwszaliczba, drugaliczba; pierwszaliczba = StrToInt(liczba1->Text); drugaliczba = StrToInt(liczba2->Text); if (dzialanie->Text == "Dodawanie") { wynik->Text = pierwszaliczba + drugaliczba; } else if (dzialanie->Text == "Odejmowanie") { wynik->Text = pierwszaliczba - drugaliczba; } else if (dzialanie->Text == "Mnożenie") { wynik->Text = pierwszaliczba * drugaliczba; } else if (dzialanie->Text == "Dzielenie") { wynik->Text = pierwszaliczba / drugaliczba; } else { ShowMessage("Wybierz działanie z listy!"); } }
Wszystko działało by świetnie, gdyby nie to, że jeśli ktoś zostawi puste pole jednej z liczb, wyświetli mu się błąd. Aby błąd nie wyglądał jakoś dziwnie, należy go zmienić, żeby użytkownik wiedział co zrobił źle - no i po Polsku :)
void __fastcall TForm1::dzialanieChange(TObject *Sender)
{
 int pierwszaliczba, drugaliczba;
 if (liczba1->Text == "") {
    ShowMessage("Podaj pierwszą liczbę");
 }
 else if (liczba2->Text == "") {
      ShowMessage("Podaj drugą liczbę");
 }
 else if (liczba1->Text == "" && liczba2->Text == "") {
      ShowMessage("Podaj dwie liczby!");
 }
 else {
 	pierwszaliczba = StrToInt(liczba1->Text);
 	drugaliczba = StrToInt(liczba2->Text);
	if (dzialanie->Text == "Dodawanie") {
		wynik->Text = pierwszaliczba + drugaliczba;
	}
	else if (dzialanie->Text == "Odejmowanie") {
		  wynik->Text = pierwszaliczba - drugaliczba;
	}
	else if (dzialanie->Text == "Mnożenie") {
		  wynik->Text = pierwszaliczba * drugaliczba;
	}
	else if (dzialanie->Text == "Dzielenie") {
		  wynik->Text = pierwszaliczba / drugaliczba;
	}
	else {
		  ShowMessage("Wybierz działanie z listy!");
	}
 }
}
Teraz już wszystko działa. Ale czy na pewno? A co jeśli użytkownik wpisze tekst zamiast liczby? Są dwa rozwiązania - pola edycji możemy zmienić na komponenty CSpinEdit z zakładki "Samples" - wtedy w takie pole można wpisać jedynie liczbę i zmieniać ją strzałkami, jednak zamiast atrybutu "Text" będzie trzeba się posłużyć atrybutem "Value". Drugie wyjście to dodatkowe zabezpieczenie w kodzie:
void __fastcall TForm1::dzialanieChange(TObject *Sender)
{
 int pierwszaliczba, drugaliczba;
 try {
	 if (liczba1->Text == "") {
		ShowMessage("Podaj pierwszą liczbę");
	 }
	 else if (liczba2->Text == "") {
		  ShowMessage("Podaj drugą liczbę");
	 }
	 else if (liczba1->Text == "" && liczba2->Text == "") {
		  ShowMessage("Podaj dwie liczby!");
	 }
	 else {
		 pierwszaliczba = StrToInt(liczba1->Text);
		 drugaliczba = StrToInt(liczba2->Text);
		 if (dzialanie->Text == "Dodawanie") {
			wynik->Text = pierwszaliczba + drugaliczba;
		 }
		 else if (dzialanie->Text == "Odejmowanie") {
			  wynik->Text = pierwszaliczba - drugaliczba;
		 }
		 else if (dzialanie->Text == "Mnożenie") {
			  wynik->Text = pierwszaliczba * drugaliczba;
		 }
		 else if (dzialanie->Text == "Dzielenie") {
			  wynik->Text = pierwszaliczba / drugaliczba;
		 }
		 else {
			  ShowMessage("Wybierz działanie z listy!");
		 }
	 }
 }
 catch (Exception &exception) {
       ShowMessage("Podana wartość jest tekstem! Musisz podać liczbę!");
 }
}
Specialnie wyróżniłem na czerwono dwa znaczniki w kodzie - try i catch. Dzięki temu możemy chronić się przed innymi rodzajami błędów, na które nie działają warunki. Korzystanie z try ... catch polega na: SPRÓBUJ WYKONAĆ { procedura } LUB JEŚLI SIĘ NIE UDA (WYŚWIETL BŁĄD: typbłędu). Typów błędów jest więcej, jednak w naszje procedurze wystarczy ustawić ogólną wartość "&anp;exception". Po kompilacji programu widać, że wszystko działa jak powinno.

Ten artykuł nie pokazuje jak ustrzedz się przed wszystkimi błędami, ponieważ wiele z nich występuje w bardziej zaawansowanych aplikacjach. Część druga, która pokaże więcej sposobów na zabezpieczanie się przed błędami, ukaże się wtedy, gdy pojawią się artykuły o większej aplikacji. Następny artykuł pokazuje jak dzięki C++ stworzyć prostą grę - strzelankę. Będzie to gra, której nie znajdzie się nigdzie w internecie - bo własna.
Autor: Michał Gacki
Główny programista i założyciel Bil Software.
Informacje o autorze | Kontakt