Funkcje¶
Deklaracja i definicja funkcji¶
Deklaracja (zwykle umieszczona w pliku nagłówkowym hpp
):
int square(int);
Definicja w pliku cpp
:
int square(int x)
{
return x * x;
}
W C++11 możliwa jest definicja funkcji wykorzystująca słowo kluczowe auto
. Typ zwracany jest deklarowany po nazwie funkcji i liście parametrów:
auto square(int x) -> int
{
return x * x;
}
Argumenty funkcji¶
Argumenty domyślne¶
Argumentom można przypisać domyślną wartość. Przy wywołaniu funkcji można pominąć argumenty posiadające wartości domyślne. Wartości domyślne mogą mieć tylko argumenty znajdujące się na końcu listy parametrów.
void f(int x, int y = 0, double z = 0.0);
f(1, 2, 3.0);
f(1, 2); // f(1, 2, 0.0)
f(1); // f(1, 0, 0.0)
Przekazywanie argumentów przez wartość¶
Najprostszy sposób przekazania argumentu do funkcji. Argument wywołania funkcji jest kopiowany do zmiennej będącej parametrem funkcji.
int f(int x)
{
x = x + 1; // nadanie lokalnej zmiennej x nowej wartości
return x;
}
int main()
{
int xx = 0;
std::cout << f(xx) << std::endl; // wyświetla 1
std::cout << xx << std::endl; // wyświetla 0; f() nie zmienia xx
}
Przekazywanie argumentów przez referencję¶
Aby funkcja mogła modyfikować swoje parametry, muszą one zostać przekazane przez referencję.
void increment(int& x)
{
x = x + 1; // nadanie zmiennej x nowej wartości
}
int main()
{
int xx = 0;
increment(xx);
std::cout << xx << std::endl; // drukuje 1;
}
Obiekty tymczasowe nie mogą być przekazywane do funkcji oczekującej referencji:
increment(xx + 1); // błąd kompilacji
Przekazywanie argumentów przez stałą referencję¶
Przekazywanie przez wartość jest wydajną techniką jeżeli przekazywane są obiekty małych rozmiarów. Jeżeli kopiowanie obiektu jest kosztowne należy przekazać argument przez stałą referencję.
void print(std::vector<double> v) // kosztowne kopiowanie obiektu
{
std::cout << "[ ";
for(int i = 0; i < v.size(); ++i) {
std::cout << v[i];
if ( i != v.size()-1 ) std::cout << ", ";
}
std::cout << "]\n";
}
std::vector<double> vd1(10); // mały wektor
std::vector<double> vd2(1000000); // duży wektor
//..
print(vd1);
print(vd2);
void print(const std::vector<double>& v) // optymalnie
{
std::cout << "[ ";
for(int i = 0; i < v.size(); ++i) {
std::cout << v[i];
if ( i != v.size()-1 ) std::cout << ", ";
}
std::cout << "]\n";
}
Obiekty tymczasowe mogą być
Funkcje przeciążone¶
Funkcje przeciążone posiadają taką samą nazwę, ale inne zestawy parametrów. Na podstawie liczby i typu parametrów wywoływana jest konkretna funkcja przeciążona.
Nie jest możliwe rozróżnienie funkcji przeciążonych jedynie po typie zwracanej wartości.
void print(double);
void print(long);
void print(const std::vector<int>&);
void f()
{
print(1L); // print(long);
print(1.0); // print(double);
print(1); // błąd! dwuznaczne wywołanie
std::vector<int> vec = { 1, 2, 3, 4 };
print(vec); // print(const std::vector<int>&);
}
Kryteria dopasowania przy wywołaniu funkcji przeciążonych¶
Kryteria dopasowania funkcji na podstawie typów parametrów:
Dokładne dopasowanie – dopuszczalna jedynie „trywialna” konwersja typów (np. nazwa tablicy na wskaźnik,
T
naconst T
)Dopasowanie dopuszczające „promocję typu” (
bool
naint
,char
naint
,short
naint
,float
nadouble
, itp.)Dopasowanie z użyciem konwersji standardowej (np.
int
nadouble
,double
naint
,Derived*
naBase*
,T*
navoid*
)Dopasowanie z użyciem konwersji zdefiniowanej przez użytkownika
Dopasowanie z użyciem elipsy (…) w deklaracji funkcji
Jeśli po dopasowana jest więcej niż jedna funkcja wywołanie jest odrzucone (błąd dwuznaczności).
Automatyczna dedukcja typu zwracanego z funkcji (C++14)¶
Dedukcja z auto
¶
W C++14 typ zwracany z funkcji może być automatycznie dedukowany z implementacji funkcji. Mechanizm dedukcji jest taki sam jak mechanizm automatycznej dedukcji typów zmiennych.
auto multiply(int x, int y)
{
return x * y;
}
Jeśli w funkcji występuje wiele instrukcji return
muszą one wszystkie zwracać wartości tego samego typu.
auto get_name(int id)
{
if (id == 1)
return "Gadget"s;
else if (id == 2)
return "SuperGadget"s;
return string("Unknown");
}
Rekurencja dla funkcji z auto
jest możliwa, o ile rekurencyjne wywołanie następuje po przynajmniej jednym wywołaniu return
zwracającego wartość nierekurencyjną.
auto factorial(int n)
{
if (n == 1)
return 1;
return factorial(n-1) * n;
}
Dedukcja z decltype(auto)
¶
Deklaracja decltype(auto)
jako typu zwracanego z funkcji powoduje zastosowanie do dedukcji typu mechanizmu decltype
(zachowującego referencje i modyfikatory const
oraz volatile
) zamiast mechanizmu auto
.
decltype(auto) get_first(const array<int, 10>& data)
{
return data[0];
}