Funkcje są potężne. Funkcje są przyjemne. Funkcje są pożyteczne. Funkcje są wygodne. Tak właśnie jest i dlatego powinniśmy starać się poznać je jak najlepiej. Tym razem przyjrzymy się bliżej słowu kluczowemu
return, które ma swoje bardzo fajne oblicze o którym nic nie wiedziałem przez bardzo długi czas zajmowania się programowaniem w C++. Co to jest takiego? Zaraz się o tym dowiesz :)
Co wiemy do tej pory o słowie kluczowym return?
Na chwilę obecną o słowie kluczowym
return powinieneś wiedzieć tyle, że może występować wewnątrz ciała funkcji i potrafi ono zwrócić wartość, którą da się odczytać w miejscu gdzie funkcja została wywołana. Z tym się nic nie zmienia i zmieniać nie będzie.
Słowo kluczowe return - nowe oblicze
Skoro przypomnieliśmy sobie co wiemy o słowie kluczowym return, zobaczmy teraz co ono jeszcze potrafi:
Jedna funkcja, wiele wystąpień return
Pierwszą nową sprawą, którą omówimy jest funkcja, w której występuje więcej niż raz słowo kluczowe
return. Pisząc funkcje, które zwracają wartość często się zdarza, że na pewnym etapie pisanego kodu wiemy z góry jaki ma zwrócić wynik operacji, a w związku z tym chcielibyśmy ją przerwać, zwracając jednocześnie interesujący nas wynik. Słowo kluczowe
return wychodzi natychmiastowo z funkcji zwracając jednocześnie wartość jaka zostanie za nim podana. Kod, który się znajdował w dalszej części funkcji nie zostanie już wykonany, bo i nie ma po co skoro funkcja obliczyła wynik, który chcieliśmy otrzymać. Przejdźmy teraz do przykładu, który zilustruje w praktyce to co zostało wyżej opisane:
bool czyGodzinaPoprawna( int godz, int min, int sek )
{
std::cout << "Jestem w funkcji czyGodzinaPoprawna" << std::endl;
if( godz < 0 || godz > 23 )
return false;
std::cout << "Godzina jest poprawna" << std::endl;
if( min < 0 || min > 59 )
return false;
std::cout << "Godziny i minuty sa poprawne" << std::endl;
if( sek < 0 || sek > 59 ) return false;
std::cout << "Czas jest poprawny" << std::endl;
return true;
}
Funkcja wypisuje komunikaty po to byś miał lepiej zobrazowany temat, który teraz poznajesz - w praktyce są one zbędne i nie powinno ich tam być.
|
Jak widać mamy funkcję, która sprawdza czy czas podany poprzez argumenty funkcji jest poprawny. Jeżeli czas jest poprawny to funkcja powinna zwrócić nam prawdę, czyli
true. W przeciwnym wypadku ma zwrócić fałsz tj.
false.
Przykładowe wywołania i ich wyniki
Zobaczmy teraz co otrzymamy na ekranie w wyniku wywołania funkcji z różnymi argumentami:
Praktyczny przykład
Zobaczmy teraz jak można w praktyce wykorzystać funkcję, która została zaprezentowana powyżej - funkcja została okrojona z komunikatów, które wypisywała:
#include <iostream>
bool czyGodzinaPoprawna( int godz, int min, int sek )
{
if( godz < 0 || godz > 23 )
return false;
if( min < 0 || min > 59 )
return false;
if( sek < 0 || sek > 59 )
return false;
return true;
}
void komunikatCzasu( bool bWynik )
{
if( bWynik )
std::cout << "Czas jest poprawny" << std::endl;
else
std::cout << "Czas jest niepoprawny" << std::endl;
}
int main()
{
komunikatCzasu( czyGodzinaPoprawna( 24, 30, 50 ) );
komunikatCzasu( czyGodzinaPoprawna( 12, 88, 50 ) );
komunikatCzasu( czyGodzinaPoprawna( 1, 00, 99 ) );
komunikatCzasu( czyGodzinaPoprawna( 7, 33, 01 ) );
return 0;
}
Standardowe wyjście programu:
Czas jest niepoprawny
Czas jest niepoprawny
Czas jest niepoprawny
Czas jest poprawny
Słowo kluczowe return w funkcjach niezwracających wartości
Skoro już wiemy co potrafi słowo kluczowe
return to zajmijmy się już formalnościami związanymi z funkcjami, które nie zwracają wartości. Słowo kluczowe
return może występować w takich funkcjach. Pełni ono taką samą rolę jak w funkcjach, które zwracają wartość z tą różnicą, że wartości żadnej nie zwraca. Skoro funkcja nie zwraca żadnej wartości to i za słowem kluczowym
return również nie umieszczamy żadnej wartości. W praktyce więc może to wyglądać tak:
#include <iostream>
void wyswietl( int tablica[], int ile )
{
if( ile <= 0 )
{
std::cout << "Tablica jest pusta." << std::endl;
return;
}
int i = 0;
do
{
std::cout << tablica[ i ] << ", ";
i++;
} while( i < ile );
std::cout << std::endl;
}
int dopisz( int iLiczba, int tablica[], int ile )
{
tablica[ ile ] = iLiczba;
ile++;
return ile;
}
int main()
{
int liczbaElementow = 0;
int tablicaLiczb[ 10 ];
wyswietl( tablicaLiczb, liczbaElementow );
liczbaElementow = dopisz( 123, tablicaLiczb, liczbaElementow );
wyswietl( tablicaLiczb, liczbaElementow );
liczbaElementow = dopisz( 321, tablicaLiczb, liczbaElementow );
wyswietl( tablicaLiczb, liczbaElementow );
return 0;
}
Wyjaśnień więcej myślę, że tu nie potrzeba, więc sprawę uważam za zamkniętą :) Przeanalizuj jednak dobrze powyższy przykład - zawiera on bardzo ciekawą obsługę dodawania elementów do tablicy, która z pewnością jest dla Ciebie dobrym elementem edukacyjnym.
Praktyczne zastosowania
O ile zastosowanie słowa kluczowego
return jest wygodne w sytuacjach, które zostały opisane o tyle jest ono
niezastąpione w momencie gdy mamy funkcję, w której mamy zagnieżdżone pętle i wewnątrz nich w pewnym momencie stwierdzimy: 'Okej, wynik już znam - wychodzimy z tej funkcji'. Robienie warunków po to by wyjść z tych zagnieżdżonych pętli to byłaby droga przez mękę, więc praktycznie zawsze w takich sytuacjach wykorzystuje się omówioną własność słowa kluczowego
return.
Zwracanie wartości przez funkcję
Pamiętaj, że funkcja, która
zwraca wartość musi zwracać ją
zawsze. Jeżeli funkcja dojdzie do końca bloku funkcji i nie napotka słowa kluczowego
return zachowanie wyjścia z funkcji
jest niezdefiniowane. Oznacza to, że aplikacja może zakończyć pracę poprzez wystąpienie błędu krytycznego aplikacji. Niektóre kompilatory łagodniej się w tych sytuacjach obchodzą i zwracają one wówczas
przypadkową wartość.
Przykład niebezpiecznej funkcji
Załóżmy teraz, że napiszemy funkcję w której nie wszystkie możliwe ścieżki przejścia będą zwracały wartość:
int funkcja( int liczba )
{
if( liczba > 10 )
return 0;
}
Próba kompilacji się powiedzie, jednak powinno pojawić się ostrzeżenie o następującej treści:
Code::Blocks - MinGW 4.4.1
In function 'int funkcja(int)':
warning: control reaches end of non-void function
Visual C++ 2008
warning C4715: 'funkcja' : not all control paths return a value
Ostrzeżenie to informuje, że funkcja nie uwzględnia wszystkich możliwych ścieżek przejścia, co w konsekwencji może dla pewnych danych spowodować
niezdefiniowane zachowanie kodu. Log kompilacji jest Twoim przyjacielem i nie należy przechodzić obok niego nigdy obojętnie. Czytaj więc go i staraj się pisać kod tak aby żadne ostrzeżenia nie pojawiały się w logu kompilacji.
A co gdy nie dostałeś ostrzeżenia dla przykładowego kodu?
Taka sytuacja również może się zdarzyć. Wówczas należy wejść w opcje kompilatora i włączyć odpowiednią opcję.
Code::Blocks
Visual C++
W przypadku Visual C++ zalecam zwiększyć poziom ostrzeżeń z 3 stopnia na 4 stopień. Użytkownicy Visual C++ powinni sobie z tym poradzić - niniejszy kurs jest poświęcony Code::Blocks, więc szczegóły zostaną w tym przypadku pominięte.