Rekurencja w C++, modyfikator const, wskaźniki i referencje jako
Transkrypt
Rekurencja w C++, modyfikator const, wskaźniki i referencje jako
SYSTEMY INFORMATYCZNE M.A. Jankowska, G. Sypniewska-Kamińska LABORATORIUM NR 02 TEMAT: REKURENCJA W C++. WYBRANE ZAAWANSOWANE ZAGADNIENIA ZWIĄZANE Z FUNKCJAMI W C++ I. Funkcje rekurencyjne Wywołanie funkcji C++ może mieć miejsce nie tylko w innej funkcji, z czym mieliśmy do czynienia dotychczas. Wywołanie funkcji może wystąpić także w jej własnej definicji. Funkcję, która wywołuje samą siebie nazywamy funkcją rekurencyjną. Przepisz, skompiluj i wykonaj zamieszczony poniżej kod programu. Przeanalizuj jego działanie. #include "stdafx.h" #include <iostream> using namespace std; int silnia_rekurencja(int n); int silnia_iteracja(int n); int _tmain(int argc, _TCHAR* argv[]) { cout << "Program oblicza wartosc funkcji silnia dla argumentow n <= 12" << endl; int n = 20; while (n >=13) { cout << "Podaj wartosc n z przedzialu [0,12]. n = "; cin >> n; } cout << endl<< "n! = " << silnia_rekurencja(n)<< endl; cout << endl<< "n! = " << silnia_iteracja(n)<< endl; return 0; } int silnia_iteracja(int n) { int s = 1; for (int i=1; i<=n; i++) s = s*i; return s; } int silnia_rekurencja(int n) { int s; // zakończenie rekurencji po spełnieniu warunku if (n == 1) return 1; s = n*silnia_rekurencja(n-1); // wywołanie funkcji silnia_rekurencja return s; } Funkcję n ! określoną dla argumentów naturalnych można zdefiniować na dwa sposoby: definicja 1 n !=1⋅2⋅...⋅(n−1)⋅n definicja 2 1 !=1, n !=n⋅(n−1)! dla n>1 Pierwsza z przytoczonych wyżej definicji funkcji n ! określa wartość funkcji w sposób bezpośredni dla każdego naturalnego argumentu n. Druga z definicji określa bezpośrednio wartość funkcji tylko dla pewnego argumentu, w tym przypadku dla n=1 . Wartości funcji dla argumentów n>1 określone są poprzez odwołanie do definicji tej samej funkcji dla argumentu mniejszego o jeden. Definicje tego typu nazywane są definicjami rekurencyjnymi. W zamieszczonym powyżej kodzie programu zdefiniowano dwie funkcje C++ realizujące to samo zadanie obliczenia wartości funkcji silnia. Funkcja silnia_iteracja oblicza wartość n ! według algorytmu iteracyjnego, wynikającego z definicji 1. Funkcja silnia_rekurencja oblicza wartość n ! zgodnie z rekurencyjną definicją 2. Funkcja ta wywołuje samą siebie. Aby ten proces nie trwał w nieskończoność, w definicji funkcji musi wystąpić przynajmniej jedna instrukcja return, realizowana po spełnieniu określonego warunku. W powyższym przykładzie rekurencyjny proces zostaje zakończony, gdy w wyniku zmniejszania Laboratorium 2 1 SYSTEMY INFORMATYCZNE argumentu funkcji o jeden przyjmie on wartość M.A. Jankowska, G. Sypniewska-Kamińska n=1 , dla której funkcja jest określona bezpośrednio. if (n==1) return 1; II. Modyfikator const Gdy stosujemy mechanizm przekazywania argumentu do funkcji przez referencję, to parametr funkcji jest traktowany jak alias przekazywanego argumentu. Oznacza to, że przy wywołaniu funkcji parametr funkcji zostaje zainicjalizowany adresem aktualnego argumentu. Funkcja uzyskuje bezpośredni dostęp do argumentu aktualnego, może go zatem zmieniać. Przekazywanie argumentów przez referencję łączy w sobie zalety przekazywania prez wskaźnik, takie jak możliwość modyfikacji argumentu przez funkcję oraz szybsze działanie (nie ma konieczności kopiowania wartości), z prostotą implementacji właściwą przekazywaniu przez wartość (nie występują operacje adresowania i wyłuskiwania). Poniżej zamieszczono kod programu wywołującego funkcję, która zmienia znak argumentu w przypadku, gdy argument ma wartość ujemną. #include "stdafx.h" #include <iostream> using namespace std; float funkcja_wb(float& x); int _tmain(int argc, _TCHAR* argv[]) { float a; cout << "Podaj dowolna liczbe wymierna: _ "; cin >>a; cout << endl<<"Jej wartosc bezwzgledna = "<< funkcja_wb(a); cout << endl<<"Po wywołaniu funkcji a = "<< a <<endl; } float funkcja_wb(float& x) { if( x < 0.0f) x = -x; return x; } Próba wywołania funkcji funkcja_wb z parametrem będącym stałą zakończy się niepowodzeniem. Jeżeli na przykład w powyższym programie umieścimy instrukcję cout << endl<<"Wartosc bezwzgledna liczby -20.5 "<< funkcja_wb(-20.5); to po kompilacji pojawi się komunikat o błędzie error C2664: 'funkcja_wb' : cannot convert parameter 1 from 'float' to 'float &' Można stosować mechanizm przekazywania parametrów przez referencję z jawną deklaracją, że argument funkcji nie zostanie zmodyfikowany. W nagłówku funkcji należy wówczas poprzedzić typ parametru formalnego kwalifikatorem const. Kompilator sprawdza (i ewentulnie wysyła komunikat o błędzie), czy w ciele funkcji parametr nie jest zmieniany pojawiając się jako lewa strona instrukcji przypisania albo w instrukcjach inkrementacji i dekrementacji. Użycie modyfikatora const zabezpiecza argument funkcji przed zmianami przy zachowaniu wszelkich zalet związanych z przekazywaniem parametrów przez referencję. W roli argumentów mogą w takim przypadku występować także stałe. float funkcja_wb1(const float& x) { float w = x; if( x < 0.0f ) w = -x; return w; } III. Wskaźniki i referencje jako wynik funkcji W wyniku działania funkcji, z wyjątkiem funkcji typu void, w miejscu jej wywołania zostaje zwrócona pojedyncza wartość określonego typu. Funkcja może zwracać jako wynik także wskaźnik albo referencję. Rozwiązania te stwarzają całkiem nowe możliwości dla programisty. Między innymi zwracanie jako wyniku wskaźnika sprawia, że funkcja może przekazać dowolną ilość danych w postaci tablicy zainicjalizowanej zwracanym wskaźnikiem. Należy zapamiętać, że przy próbie zwrócenia adresu lokalnej zmiennej automatycznej funkcji wygenerowane zostanie ostrzeżenie, a program nie będzie działał poprawnie. Rozwiązaniem jest utworzenie za pomocą operatora new zmiennej/zmiennych wskaźnikowej w obszarze pamięci wolnej (na stercie). Laboratorium 2 2 SYSTEMY INFORMATYCZNE M.A. Jankowska, G. Sypniewska-Kamińska Poniżej zamieszczono kod programu wywołującego funkcję, która przydziela pamięć jednowymiarowej tablicy dynamicznej oraz drukuje wartości elementów tego wektora na ekranie. #include "stdafx.h" #include <iostream> #include <conio.h> using namespace std; double * rozlokuj_wektor (int nn); void wypisz_wektor (double * x, int nn); int _tmain(int argc, _TCHAR* argv[]) { int n; cout << "Okresl liczbe elemntow wektora n = "; cin >> n; double * w; w = rozlokuj_wektor(n); for (int i = 0; i<n; i++) { w[i] = pow(static_cast<double>(i), 1.0/3.0); cout <<i<<" : "<< w[i] <<endl; } cout << endl << "Elementy wektora w:" << endl; wypisz_wektor(w,n); delete [] w; _getch(); return 0; } double * rozlokuj_wektor (int nn) { double * ptab = new double[nn]; return ptab; } void wypisz_wektor (double * x, int nn) { for (int i=0; i<nn; i++) cout << x[i] << " "; cout << endl; } Laboratorium 2 3