Last modified: July 06, 2019
This article is written in: 🇵🇱
Funkcje są jednym z kluczowych narzędzi w programowaniu, które pozwalają na podzielenie kodu na mniejsze, zarządzalne części. Funkcje składają się z deklaracji (nazwa, typ zwracany, argumenty) i definicji (ciało funkcji).
Podstawowe składniki funkcji to:
Struktura funkcji:
typ_zwracanej_wartości nazwa_funkcji(typ_argumentu nazwa_argumentu, ...) {
// ciało funkcji
return wartość; // zwracana wartość (jeśli funkcja nie jest typu void)
}
int main() {
typ_zwracanej_wartości wynik = nazwa_funkcji(argumenty...);
return 0;
}
Przykład:
#include <iostream>
// definicja funkcji fun1
void fun1() { std::cout << "fun1" << std::endl; }
// deklaracja funkcji fun2 (jej definicja znajduje się poniżej funkcji main)
void fun2();
int main() {
fun1(); // wywołanie fun1 - działa, ponieważ fun1 została zdefiniowana wcześniej
fun2(); // wywołanie fun2 - również działa, mimo że definicja jest poniżej, dzięki wcześniejszej deklaracji
return 0;
}
// definicja funkcji fun2 (po funkcji main)
void fun2() { std::cout << "fun2" << std::endl; }
// UWAGA: fun3 nie została ani zadeklarowana, ani zdefiniowana przed funkcją main, dlatego jej wywołanie by się nie powiodło
W praktyce dobre jest umieszczanie deklaracji funkcji w plikach nagłówkowych (.h lub .hpp), a ich definicji w plikach źródłowych (.cpp), aby zachować porządek i modularność kodu.
Funkcje, które zwracają wartość, muszą określić swój typ zwracanej wartości w deklaracji. Aby zwrócić wartość, używamy słowa kluczowego return
, które kończy wykonywanie funkcji i zwraca określoną wartość. Typ zwracanej wartości musi być zgodny z deklarowanym typem funkcji.
Przykład:
#include <iostream>
int suma(int x, int y) { return x + y; }
int main() {
int a = 5;
int b = 3;
std::cout << "Suma a + b: " << suma(a, b) << std::endl;
return 0;
}
Funkcje typu void nie zwracają wartości. Słowo kluczowe return nie jest wymagane, chociaż może być używane do wczesnego zakończenia funkcji.
Przykład:
#include <iostream>
void wypisz_imie(const std::string& s) { std::cout << "Imię: " << s << std::endl; }
int main() {
std::string imie = "Karol";
wypisz_imie(imie);
return 0;
}
Funkcje w C++ mogą mieć parametry z domyślnymi wartościami. Jeśli taki parametr nie jest dostarczany podczas wywoływania funkcji, używana jest jego domyślna wartość. Ułatwia to tworzenie funkcji bardziej elastycznych, które można dostosowywać do różnych potrzeb.
Przykład:
#include <iostream>
int pomnoz(int a, int b = 3) { return a * b; }
int main() {
int x = 2;
int y = 7;
std::cout << "x * y: " << pomnoz(x, y) << std::endl; // Używając wartości y jako drugiego argumentu
std::cout << "x * 3: " << pomnoz(x) << std::endl; // Używając domyślnej wartości dla drugiego argumentu
return 0;
}
Uwaga: Jeśli chcemy ustawić domyślną wartość dla jednego parametru, wszystkie kolejne parametry (po nim) także muszą mieć domyślne wartości.
Podczas projektowania funkcji w C++, możemy wybrać, czy chcemy przekazać argumenty przez wartość, czy przez referencję.
Przekazując argument przez wartość, funkcja otrzymuje kopię wartości argumentu. Wszelkie modyfikacje tej kopii w ciele funkcji nie wpływają na oryginalną zmienną.
Przykład:
#include <iostream>
void pomnoz(int a, int b) { a = a * b; }
int main() {
int x = 2;
pomnoz(x, 3);
std::cout << x << std::endl; // Wypisuje 2, ponieważ wartość x nie została zmieniona w funkcji pomnoz
return 0;
}
Jeśli argument jest przekazywany przez referencję, funkcja pracuje bezpośrednio na oryginalnej zmiennej, a nie na jej kopii. Dlatego modyfikacje tej zmiennej w ciele funkcji wpływają na oryginalną zmienną.
Przykład:
#include <iostream>
void pomnoz(int &a, int b) { a = a * b; }
int main() {
int x = 2;
pomnoz(x, 3);
std::cout << x << std::endl; // Wypisuje 6, ponieważ wartość x została zmieniona w funkcji pomnoz
return 0;
}
Uwaga: Referencje są idealne do przekazywania obiektów o dużym rozmiarze (np. duże wektory lub macierze), ponieważ unika się wtedy kosztownego kopiowania. Jednak trzeba być ostrożnym, aby nie modyfikować przekazanych danych bez zamierzenia. Jeśli chcesz użyć referencji, ale nie chcesz modyfikować oryginalnych danych, możesz użyć referencji stałej:
void funkcja(const int ¶m) {
// można odczytać wartość param, ale nie można jej modyfikować
}
Dzięki temu funkcja ma dostęp do oryginalnych danych bez konieczności ich kopiowania, ale jednocześnie jest pewność, że dane nie zostaną przypadkowo zmienione wewnątrz funkcji.