Funkcje w C++

Funkcje w C++ to podstawowe bloki budujące program, które pozwalają na podział kodu na mniejsze, zarządzalne fragmenty. Każda funkcja może wykonywać określone zadanie i może być wywoływana z różnych miejsc w programie. Użycie funkcji ułatwia zarządzanie kodem, jego testowanie oraz ponowne użycie.
Definicja Funkcji
Funkcja składa się z nagłówka i ciała. Nagłówek określa nazwę funkcji, typ zwracany oraz parametry (jeśli istnieją). Ciało funkcji, otoczone klamrami {}, zawiera kod, który ma zostać wykonany podczas wywołania funkcji.
Deklaracja funkcji
Deklaracja funkcji (czasami nazywana prototypem funkcji) informuje kompilator o nazwie funkcji, jej typie zwracanym oraz typach i liczbie jej parametrów. Deklaracja funkcji nie zawiera ciała funkcji (jej implementacji). Składnia:
typ_zwracany nazwa_funkcji(typ_parametru1 nazwa_parametru1, typ_parametru2 nazwa_parametru2, ...) {
    // ciało funkcji
}
Przykład:
int dodaj(int, int); // Deklaracja funkcji 'dodaj'
Definicja funkcji
Definicja funkcji zawiera kompletny kod funkcji, w tym jej ciało, które określa, co funkcja ma robić. Definicja realizuje to, co zostało zadeklarowane w deklaracji funkcji. Składnia:
typ_zwracany nazwa_funkcji(typ_parametru1 nazwa_parametru1, typ_parametru2 nazwa_parametru2, ...) {
    // Ciało funkcji
}
Przykład:
// Definicja funkcji 'dodaj'
int dodaj(int a, int b) {
    return a + b;
}
Funkcje zwrotne i bez zwrotne
Funkcje zwrotne
Funkcje zwrotne (zwracające wartość) wykonują operacje na danych wejściowych i zwracają wynik. Typ zwracany musi być zgodny z deklaracją funkcji. Przykład:
double srednia(double x, double y) {
    return (x + y) / 2.0;
}
Funkcje bez zwrotne
Funkcje bez zwrotne nie zwracają wartości do miejsca wywołania. Są zadeklarowane z typem zwracanym void. Mogą wykonywać operacje, takie jak wyświetlanie danych, ale nie zwracają wyniku. Przykład:
void wyswietlWiadomosc() {
    std::cout << "C++ jest super!" << std::endl;
}
Przykłady

Funkcja zwracająca wartość

// Deklaracja
double obliczPole(double, double);

// Definicja
double obliczPole(double szerokosc, double wysokosc) {
    return szerokosc * wysokosc;
}

// Użycie
int main() {
    double pole = obliczPole(5.0, 10.0);
    std::cout << "Pole: " << pole << std::endl;
    return 0;
}
Funkcja bez zwrotu
// Deklaracja
void pokazLicznik(int);

// Definicja
void pokazLicznik(int licznik) {
    std::cout << "Licznik: " << licznik << std::endl;
}

// Użycie
int main() {
    for(int i = 0; i < 3; i++) {
        pokazLicznik(i);
    }
    return 0;
}
Funkcje z parametrami domyślnymi w C++
Funkcje z parametrami domyślnymi w C++ pozwalają na określenie wartości domyślnych dla argumentów funkcji, co umożliwia wywołanie funkcji bez podawania wszystkich argumentów. Jeśli podczas wywołania funkcji pominiemy jeden lub więcej argumentów, zostaną użyte zdefiniowane wartości domyślne. To sprawia, że funkcje są bardziej elastyczne i mogą być wywoływane w różnych kontekstach bez konieczności definiowania wielu przeciążeń funkcji. Przykład: Rozważmy funkcję, która oblicza pole powierzchni prostokąta. Możemy określić jeden z wymiarów jako mający wartość domyślną, co pozwoli na użycie tej samej funkcji do obliczania pola kwadratu.
#include <iostream>

// Deklaracja funkcji z parametrami domyślnymi
double poleProstokata(double szerokosc, double wysokosc = 1.0) {
    return szerokosc * wysokosc;
}

int main() {
    std::cout << "Pole prostokata 5x4: " << poleProstokata(5, 4) << std::endl; // Wywołanie z dwoma argumentami
    std::cout << "Pole kwadratu o boku 5: " << poleProstokata(5) << std::endl; // Wywołanie z jednym argumentem, drugi jest domyślny
    
    return 0;
}
W tym przykładzie:
  • Funkcja poleProstokata może być wywołana albo z dwoma argumentami, aby obliczyć pole prostokąta, albo z jednym argumentem, w którym przypadku obliczane jest pole kwadratu (dzięki zastosowaniu wartości domyślnej dla drugiego parametru).
  • Przy wywołaniu poleProstokata(5) używana jest wartość domyślna dla wysokosc, która wynosi 1.0, co sprawia, że funkcja oblicza pole kwadratu o boku 5.
Funkcje z parametrami domyślnymi czynią kod bardziej zwięzłym i zwiększają jego czytelność, umożliwiając jednocześnie zachowanie elastyczności w zakresie sposobu wywoływania funkcji.
Zasady definiowania funkcji z parametrami domyślnymi:
  • Parametry domyślne są określane w prototypie funkcji lub przy jej definicji, ale nie w obu miejscach jednocześnie.
  • Wartości domyślne dla parametrów są podawane od prawej strony listy parametrów, co oznacza, że każdy parametr mający wartość domyślną musi być poprzedzony przez parametr z wartością domyślną lub parametr, dla którego argument musi być podany przy wywołaniu.
  • Nie można pominąć argumentu mającego wartość domyślną, jeśli po nim występuje argument, dla którego wartość musi być podana.
Przeciążenie Funkcji w C++
Przeciążenie funkcji to cecha języka C++, która pozwala na definiowanie wielu funkcji o tej samej nazwie, ale różniących się listą argumentów. Funkcje te mogą różnić się typem, liczbą lub kolejnością parametrów. Mechanizm ten zwiększa elastyczność języka, umożliwiając wykorzystanie tej samej nazwy funkcji do wykonywania podobnych zadań, ale w różnych kontekstach lub z różnymi typami danych.
Zasady Przeciążenia Funkcji
  • Funkcje muszą różnić się sygnaturą, czyli typem, liczbą lub kolejnością parametrów.
  • Funkcje nie mogą różnić się jedynie typem zwracanym. Kompilator wybiera, którą funkcję wywołać, na podstawie argumentów przekazanych podczas wywołania, a nie na podstawie oczekiwanego typu zwracanego.
  • Dobre praktyki wymagają, aby wszystkie przeciążone wersje funkcji wykonywały semantycznie podobne operacje, aby uniknąć zamieszania wśród osób korzystających z kodu.
Przykłady Przeciążenia Funkcji
Przeciążenie na podstawie liczby argumentów
#include <iostream>
using namespace std;

void wyswietl(int i);
void wyswietl(double d);
void wyswietl(int i, double d);

int main() {
    wyswietl(10);      // Wywołuje funkcję wyswietl(int)
    wyswietl(10.5);    // Wywołuje funkcję wyswietl(double)
    wyswietl(10, 10.5); // Wywołuje funkcję wyswietl(int, double)
    return 0;
}
void wyswietl(int i) {
    cout << "Wyswietlam int: " << i << endl;
}

void wyswietl(double d) {
    cout << "Wyswietlam double: " << d << endl;
}

void wyswietl(int i, double d) {
    cout << "Wyswietlam int i double: " << i << ", " << d << endl;
}
Przeciążenie na podstawie typu argumentów
#include <iostream>
using namespace std;

void wyswietl(char c);

// Poprzednie definicje funkcji wyswietl() pozostają bez zmian

int main() {
    wyswietl('A'); // Wywołuje funkcję wyswietl(char)
    // Pozostałe wywołania funkcji jak w poprzednim przykładzie
}
void wyswietl(char c) {
    cout << "Wyswietlam char: " << c << endl;
}
Zastosowanie Przeciążenia Funkcji
Przeciążenie funkcji jest szeroko stosowane w bibliotekach i aplikacjach, aby zapewnić intuicyjne interfejsy do wykonywania podobnych zadań na różnych typach danych lub różnej liczbie argumentów. Przykłady zastosowań to:
  • Operacje matematyczne, takie jak abs() lub max(), które mogą być stosowane do różnych typów numerycznych.
  • Funkcje tworzące lub przetwarzające struktury danych, które mogą przyjmować różne typy danych lub różną liczbę argumentów do określenia szczegółów operacji.
  • Metody w klasach, które oferują różne sposoby inicjalizacji obiektów lub wykonania operacji w zależności od dostępnych danych.