W ostatnim rozdziale zdefiniowaliśmy funkcję jako zbiór instrukcji wykonywanych sekwencyjnie. Chociaż jest to z pewnością prawdą, definicja ta nie zapewnia zbyt dużego wglądu w to, dlaczego funkcje są przydatne. Zaktualizujmy naszą definicję: A funkcja to ciąg instrukcji wielokrotnego użytku, zaprojektowany do wykonania określonego zadania.
Wiesz już, że każdy program wykonywalny musi mieć funkcję o nazwie main() (która jest miejscem, w którym program rozpoczyna wykonywanie po uruchomieniu). Jednakże w miarę jak programy stają się coraz dłuższe, zarządzanie całym kodem w funkcji main() staje się coraz trudniejsze. Funkcje umożliwiają nam dzielenie naszych programów na małe, modułowe części, które są łatwiejsze do zorganizowania, testowania i używania. Większość programów korzysta z wielu funkcji. Standardowa biblioteka C++ zawiera mnóstwo już napisanych funkcji, z których możesz skorzystać — jednak równie powszechne jest pisanie własnych. Funkcje, które sam piszesz, nazywane są funkcjami zdefiniowanymi przez użytkownika.
Rozważmy przypadek, który może mieć miejsce w prawdziwym życiu: czytasz książkę, kiedy przypominasz sobie, że musisz wykonać telefon. Umieszczasz zakładkę w swojej książce, wykonujesz połączenie telefoniczne, a kiedy skończysz rozmowę telefoniczną, wracasz do miejsca, w którym zaznaczyłeś zakładkę i kontynuujesz książkę dokładnie od miejsca, w którym ją przerwałeś.
Programy w C++ mogą działać w ten sam sposób (i zapożyczają trochę tej samej nomenklatury). Program będzie wykonywał instrukcje sekwencyjnie w obrębie jednej funkcji, gdy napotka wywołanie funkcji. A wywołanie funkcji nakazuje procesorowi przerwanie bieżącej funkcji i wykonanie innej funkcji. CPU zasadniczo „zapisuje zakładkę” w bieżącym momencie wykonania, wykonuje funkcję nazwaną w wywołaniu funkcji, a następnie zwraca do miejsca, w którym zaznaczył zakładkę i wznawia wykonywanie.
Nomenklatura
Funkcją inicjującą wywołanie funkcji jest gość, a funkcją zwany (wykonywaną) jest wywoływany. Wywołanie funkcji jest czasami nazywane wywołaniem, przy czym wywołujący wywołuje odbiorcę.
Przykład funkcji zdefiniowanej przez użytkownika
Najpierw zacznijmy od najbardziej podstawowej składni, aby zdefiniować funkcję zdefiniowaną przez użytkownika. W ciągu następnych kilku lekcji wszystkie funkcje zdefiniowane przez użytkownika będą miały następującą formę:
returnType functionName() // This is the function header (tells the compiler about the existence of the function)
{
// This is the function body (tells the compiler what the function does)
}Pierwszy wiersz nazywa się nieformalnie nagłówkiem funkcji i informuje kompilator o istnieniu funkcji, jej nazwie i kilku innych informacjach, które omówimy w przyszłych lekcjach (takich jak typ zwracany).
- W tej lekcji będziemy używać a returnType z
int(dla funkcjimain()) lubvoid(w przeciwnym razie). Na razie nie przejmuj się nimi, ponieważ w następnej lekcji porozmawiamy więcej o typach zwracanych wartości i zwracanych wartościach (2.2 -- Wartości zwracane przez funkcję (funkcje zwracające wartość)). - Podobnie jak zmienne mają nazwy, tak samo funkcje zdefiniowane przez użytkownika. Nazwafunkcji to nazwa (identyfikator) funkcji zdefiniowanej przez użytkownika.
- Nawiasy po identyfikatorze informują kompilator, że definiujemy funkcję funkcja.
Nawiasy klamrowe i instrukcje pomiędzy nimi nazywane są ciało funkcyjne. Tutaj znajdują się instrukcje określające działanie funkcji.
Aby wywołać funkcję, używamy nazwy funkcji, po której następuje zestaw nawiasów (np. functionName() wywołuje funkcję, której nazwa to functionName). są umieszczane obok nazwy funkcji (bez odstępów między nimi).
Na razie funkcja musi zostać zdefiniowana, zanim będzie można ją wywołać. Sposoby obejścia tego problemu omówimy na lekcji 2.7 -- Deklaracje przesyłania dalej i definicje.
Oto przykładowy program ilustrujący definiowanie i wywoływanie funkcji zdefiniowanej przez użytkownika:
#include <iostream> // for std::cout
// Definition of user-defined function doPrint()
// doPrint() is the called function in this example
void doPrint()
{
std::cout << "In doPrint()\n";
}
// Definition of user-defined function main()
int main()
{
std::cout << "Starting main()\n";
doPrint(); // Interrupt main() by making a function call to doPrint(). main() is the caller.
std::cout << "Ending main()\n"; // This statement is executed after doPrint() ends
return 0;
}Ten program generuje następujące dane wyjściowe:
Starting main() In doPrint() Ending main()
Ten program rozpoczyna wykonywanie od góry funkcji main() i od pierwszej linii do wykonania drukuje Starting main().
Druga linia w main() jest wywołaniem funkcji doPrint(). Wiemy, że jest to wywołanie funkcji, dzięki nawiasom końcowym.
Ostrzeżenie
Wywołując funkcję, nie zapomnij o nawiasach () po nazwie funkcji. Jeśli zapomnisz nawiasów, Twój program może się nie skompilować (a jeśli tak, funkcja nie zostanie wywołana).
Ponieważ wykonano wywołanie funkcji, wykonywanie instrukcji w main() zostaje wstrzymane, a wykonanie przeskakuje na początek wywoływanej funkcji doPrint(). Pierwsza (i jedyna) linia w doPrint() wypisuje In doPrint(). Podczas oceny doPrint() kończy się, wykonanie wraca do wywołującego (main()) i jest kontynuowane od punktu tuż za wywołaniem funkcji. W rezultacie następna instrukcja wykonana w main() wypisuje Ending main().
Wywoływaniu funkcji więcej niż raz
Jedną z przydatnych cech funkcji jest to, że można je wywoływać więcej niż raz. Oto program, który to demonstruje:
#include <iostream> // for std::cout
void doPrint()
{
std::cout << "In doPrint()\n";
}
// Definition of function main()
int main()
{
std::cout << "Starting main()\n";
doPrint(); // doPrint() called for the first time
doPrint(); // doPrint() called for the second time
std::cout << "Ending main()\n";
return 0;
}Ten program generuje następujące dane wyjściowe:
Starting main() In doPrint() In doPrint() Ending main()
Ponieważ doPrint() jest wywoływany dwukrotnie main(), doPrint() wykonuje się dwukrotnie i In doPrint() jest wypisywany dwukrotnie (raz na każde wywołanie).
Funkcje mogą wywoływać funkcje, które wywołują inne funkcje
Widzieliście już, że funkcja main() może wywoływać inne funkcje (takie jak funkcja doPrint() w powyższym przykładzie). Funkcje wywoływane przez main() mogą także wywoływać inne funkcje (i te funkcje mogą też wywoływać funkcje itd.). W poniższym programie funkcja main() wywołuje funkcję doA(), która wywołuje funkcję doB():
#include <iostream> // for std::cout
void doB()
{
std::cout << "In doB()\n";
}
void doA()
{
std::cout << "Starting doA()\n";
doB();
std::cout << "Ending doA()\n";
}
// Definition of function main()
int main()
{
std::cout << "Starting main()\n";
doA();
std::cout << "Ending main()\n";
return 0;
}Ten program generuje następujące dane wyjściowe:
Starting main() Starting doA() In doB() Ending doA() Ending main()
Funkcje zagnieżdżone nie są obsługiwane
Funkcja, której definicja jest umieszczona wewnątrz innej funkcji, jest funkcją zagnieżdżoną. W przeciwieństwie do niektórych innych języków programowania, w C++ funkcji nie można zagnieżdżać. Następujący program jest nielegalny:
#include <iostream>
int main()
{
void foo() // Illegal: this function is nested inside function main()
{
std::cout << "foo!\n";
}
foo(); // function call to foo()
return 0;
}Właściwy sposób napisania powyższego programu jest następujący:
#include <iostream>
void foo() // no longer inside of main()
{
std::cout << "foo!\n";
}
int main()
{
foo();
return 0;
}Nomenklatura
„foo” to pozbawione znaczenia słowo, które jest często używane jako nazwa zastępcza funkcji lub zmiennej, gdy nazwa nie jest istotna dla demonstracji jakiejś koncepcji. Takie słowa nazywane są zmiennymi metasyntaktycznymi (chociaż w języku potocznym często nazywa się je „nazwami zastępczymi”, ponieważ nikt nie pamięta terminu „zmienna metasyntaktyczna”). Inne popularne zmienne metasyntaktyczne w C++ to „bar”, „baz” i trzyliterowe słowa zakończone na „oo”, takie jak „goo”, „moo” i „boo”).
Dla osób zainteresowanych etymologią (jak ewoluują słowa), RFC 3092 jest interesującą lekturą.
Czas quizu
Pytanie nr 1
W definicji funkcji, jakie są nazywane nawiasami klamrowymi i instrukcjami pośrednimi?
Pytanie nr 2
Co wypisuje poniższy program? Nie kompiluj tego programu, po prostu prześledź kod samodzielnie.
#include <iostream> // for std::cout
void doB()
{
std::cout << "In doB()\n";
}
void doA()
{
std::cout << "In doA()\n";
doB();
}
// Definition of function main()
int main()
{
std::cout << "Starting main()\n";
doA();
doB();
std::cout << "Ending main()\n";
return 0;
}
