Zasada Sturgeona - 90% czegokolwiek nie nadaje się do użytku.
C++: Funkcje użytkownika

Funkcję możemy rozumieć jako podprogramami wykonujące pewne konkretne zadanie. Funkcja może być wywoływane przez inne funkcje. Może pobierać parametry, na których wykonuje jakieś działania, mogą również zwracać wartości. Zawsze, kiedy w programie mamy powtarzający się kod, warto z niego stworzyć funkcję, a następnie tylko ją wywoływać.


Dwa pojęcia są niezbędne dla zrozumienia dalszej części opisu:
  • Deklaracja (prototyp) funkcji - to zapis informujący kompilator, że gdzieś w programie znajduje się nasza funkcja zwracająca określony typ wartości i pobierająca określoną ilość i typ parametrów. Deklaracja nie stanowi właściwej zawartością funkcji.
  • Definicja funkcji - określa co ma ona robić, czyli jest to właściwy jej kod.
Nie wszystkie i nie zawsze funkcja musi być deklarowane. Jeśli jednak chcemy użyć (wywołać) funkcję przed jej definicją lub kod funkcji znajduje się w innym module (pliku) to deklaracja jest bezwzględnie konieczna.
 
Deklaracja funkcji
Ogólna składnia deklaracji (prototypu) funkcji wygląda następująco:
 
typ identyfikator(parametry);
  • typ określa jakiego rodzaju wartość zwraca funkcja (może to być dowolny typ języka, z wyjątkiem tablicy i funkcji);
  • identyfikator to nazwa funkcji, pod jaką będziemy ją wywoływać;
  • parametry (opcjonalne) - określają argumenty, jakie muszą być przekazane do funkcji, np.:
   double pole(double a, double b);

Jeśli nie podamy typu zwracanej wartości, domyśle zostanie on określony przez kompilator jako int. Jeżeli funkcja wymaga kilku parametrów, to na liście oddzielamy je przecinkami. Jeśli funkcja nie wymaga żadnych parametrów, w nawiasach piszemy void lub pozostawiamy puste nawiasy (muszą one wystąpić zawsze w deklaracji i definicji funkcji). Przy określaniu listy parametrów, identyfikatory są opcjonalne. Dodatkowo można przy ich deklaracji użyć słowa kluczowego const, które gwarantuje, że wartość wskazanych danych nie zostanie w funkcji zmieniona. Oto kilka przykładów:

   int fun1(int);
   void fun2(int, double);
   fun3(void);
   char fun4();
   double fun5(const int x);

 
Definicja funkcji

W definicji funkcji podajemy właściwy jej kod, czyli to co ma ona wykonywać. Składnia wygląda tak:
 
[specyfikator1] typ nazwa_funkji([lista_parametrów])
{
  ciało_funkcji
}
  • Specyfikator1 (opcjonalny) - może to być słowa: extern, które oznacza, że funkcja ma być dostępna również w innych plikach projektu lub static - oznaczające, że funkcja będzie dostępna tylko w obrębie pliku, gdzie została zadeklarowana.
  • ciało_funkcji - właściwy kod funkcji.
Definicja funkcji musi się zgadzać z jej deklaracją (identyczne muszą być: typ zwracanej wartości, nazwa funkcji i lista parametrów) Zakończenie wykonywania funkcji i zwrócenie wartości wykonuje instrukcja return. Oto kilka przykładów:
   double pole(double a, double b)
   {   return a * b;
   }

   long kwadrat(int x)
   {   return x * x;
   }
Uwaga: deklaracja funkcji zazwyczaj wygląda tak, jak jej pierwsza linijka definicji (określenie nazwy, typu zwracanej wartości i parametrów). Jest jednak istotna różnica. Deklaracja zawsze musi kończyć się średnikiem, natomiast po definicji piszemy nawiasy klamrowy i wewnątrz nich wstawiamy właściwy kod funkcji.
Tak utworzoną funkcję można już wykorzystywać. Wywołujemy je np. w następujący sposób:
   printf("Pole prostokąta o bokach 10 i 5 wynosi %d ", pole(10, 5));
   x=kwadrat(5);
Funkcje przeciążone i parametry domyśle

Język C++ umożliwia tworzenie funkcji przeciążonych i funkcji, które posiadają domyślne wartości parametrów. Teraz kila słów wyjaśnienia na ten temat. Funkcję nazywamy przeciążoną, jeżeli w programie istnieje jeszcze inna funkcja o takiej samej nazwie, tylko z innymi parametrami i/lub typem zwracanej wartości. To, która funkcja zostanie wykonana podczas wywołania zależy od liczby i rodzaju podanych przez nas parametrów. Dlatego nie wolno tworzyć dwóch funkcji o takich samych nazwach i parametrach, a jedynie różnych typach zwracanych wartości. Jeśli nie zostanie odnaleziona żadna funkcja, której liczba parametrów i ich typy zgadza się z podaną przez nas, zajdzie szereg standardowych konwersji i któraś z funkcji zostanie wykonana. Oto przykład:
   double pole(double a, double b)
   {  return a * b;
   }

   double pole(double r)
   {   return 3.14 * r * r;
   }

C++ umożliwia nam również tworzenie funkcji, w których pewne parametry mogą być pominięte podczas jej wywoływania. Przy ich braku, przyjmą wartości domyślne. Deklarując taką funkcję musimy parametry, które mają przyjąć wartości domyślne zainicjować jakąś wartością. Należy pamiętać, że z chwilą utworzenia parametru domyślnego, wszystkie następne w deklaracji również muszą być domyślne, np.:

double funkcja(double a = 10, int b = 5);
Wywołanie funkcji:
   funkcja();
jest równoważne wywołaniu funkcji:
   funkcja(10,5);
a wywołanie:
   funkcja(2);
jest równoważne:
   funkcja(2,5);

 
Przekazywanie parametrów do funkcji

Istnieją różne sposoby przekazywania parametrów do funkcji. Najprostszą i najczęściej wykorzystywaną jest przekazywanie przez wartość. Przy przekazywaniu parametrów w ten sposób funkcja tworzy lokalną kopię zmiennej danego typu i zapamiętuje tam przekazaną wartość. Z tego względu wewnątrz funkcji, można dowolnie modyfikować wartość tej zmiennej (chyba, że ma atrybut const), a wartość zmiennej przekazanej nie ulegnie zmianie. Najlepiej zobrazuje to poniższy przykład:
   #include <stdio.h>

   double pole(double a, double b)
   {   a++;
       b++;
       return a * b;
   }

   void funkcja(void)
   {   double a, b, p;
       a = 5;
       b = 7;
       p = pole(a, b);
       printf("Pole prostokąta o bokach %fx%f zwiększonych o 1 wynosi %f",
	           a, b, p);
       return 0;
   }
Po wykonaniu funkcji pole wartość zmiennych a i b nie uległa zmianie.
Przez wartość można również przekazywać całe tablice. Należy jednak pamiętać, że przekazanie tablicy do funkcji przez wartość powoduje stworzenie jej kopii. Łatwo jest doprowadzić do przepełnienia pamięci, szczególnie gdy wykorzystujemy rekurencję.

Drugi sposób przekazywania parametrów do funkcji, to przekazywanie przez wskaźnik polegający na podaniu przy deklarowanym parametrze operatora wyłuskania (*). Polega to na tym, że zamiast tworzyć kopię zmiennej w wywołanej funkcji przekazujemy jedynie jej adres. Wykorzystywane jest to głównie w dwóch przypadkach: kiedy chcemy przekazać do funkcji całe tablice, albo kiedy funkcja musi zwrócić więcej niż jedną wartość (zmiana wartości zmiennej przekazanej jako wskaźnik powoduje jej faktyczną zmianę, czyli można zmieniać z poziomu jednej funkcji zmienne deklarowane w innej. Nie wolno przekazywać do funkcji, które pobierają parametry przez adres stałych, gdyż one nie posiadają swoich adresów. Oto przykład:

   #include <stdio.h>

   int wyswietl(char *napis)
   {   printf("%s",napis);
       return 0;
   }

   int main(void)
   {   char tekst[]="To jest tekst do wyświetlenia";
       wyswietl(tekst);
       return 0;
   }

Ostatni sposób przekazywania wartości do funkcji to przekazywanie przez referencję wymagający podania przy deklarowanym parametrze operatora referencji (&). Jest to jeszcze szybsza metoda niż wskaźniki. Przypomnę tylko, że zmienna referencyjna to synonim innej zmiennej. Oto przykład:

   #include <stdio.h>

   int wyswietl(char &napis)
   {   printf("%s",napis);
       return 0;
   }

   int main(void)
   {   char tekst[]="To jest tekst do wyświetlenia";
       wyswietl(tekst);
       return 0;
   }
« wstecz   dalej »