W poprzedniej lekcji dowiedzieliśmy się, że funkcja może zwracać wartość do osoby wywołującej funkcję. Użyliśmy tego do stworzenia modułowej getValueFromUser , której użyliśmy w tym programie:
#include <iostream>
int getValueFromUser()
{
std::cout << "Enter an integer: ";
int input{};
std::cin >> input;
return input;
}
int main()
{
int num { getValueFromUser() };
std::cout << num << " doubled is: " << num * 2 << '\n';
return 0;
}A co by było, gdybyśmy chcieli umieścić linię wyjściową również w jej własnej funkcji? Możesz spróbować czegoś takiego:
#include <iostream>
int getValueFromUser()
{
std::cout << "Enter an integer: ";
int input{};
std::cin >> input;
return input;
}
// This function won't compile
void printDouble()
{
std::cout << num << " doubled is: " << num * 2 << '\n';
}
int main()
{
int num { getValueFromUser() };
printDouble();
return 0;
}To się nie skompiluje, ponieważ funkcja printDouble nie wie, jaki identyfikator num jest. Możesz spróbować zdefiniować num jako zmienną wewnątrz funkcji printDouble():
void printDouble()
{
int num{}; // we added this line
std::cout << num << " doubled is: " << num * 2 << '\n';
}Chociaż rozwiązuje to błąd kompilatora i umożliwia kompilację programu, program nadal nie działa poprawnie (zawsze wypisuje „0 podwojone to: 0”). Sednem problemu jest to, że funkcja printDouble nie ma możliwości dostępu do wartości wprowadzonej przez użytkownika.
Musimy w jakiś sposób przekazać wartość zmiennej num do funkcji printDouble tak, że printDouble możemy użyć tej wartości w treść funkcji.
Parametry i argumenty funkcji
W wielu przypadkach przydatna jest możliwość przekazania informacji Do o wywoływanej funkcji, aby funkcja miała dane do pracy. Na przykład, jeśli chcemy napisać funkcję dodającą dwie liczby, potrzebujemy sposobu, aby powiedzieć funkcji, które dwie liczby mają zostać dodane, gdy ją wywołamy. W przeciwnym razie skąd funkcja wiedziałaby, co dodać? Robimy to poprzez parametry i argumenty funkcji.
A parametr funkcji to zmienna używana w nagłówku funkcji. Parametry funkcji działają niemal identycznie jak zmienne zdefiniowane wewnątrz funkcji, z jedną różnicą: są inicjowane wartością podaną przez osobę wywołującą funkcję.
Parametry funkcji definiuje się w nagłówku funkcji poprzez umieszczenie ich pomiędzy nawiasami po nazwie funkcji, przy czym poszczególne parametry oddziela się przecinkami.
Oto kilka przykładów funkcji z różną liczbą parametrów:
// This function takes no parameters
// It does not rely on the caller for anything
void doPrint()
{
std::cout << "In doPrint()\n";
}
// This function takes one integer parameter named x
// The caller will supply the value of x
void printValue(int x)
{
std::cout << x << '\n';
}
// This function has two integer parameters, one named x, and one named y
// The caller will supply the value of both x and y
int add(int x, int y)
{
return x + y;
}An argument to wartość, która jest przekazał z osobę wywołującą Do funkcję podczas wywołania funkcji:
doPrint(); // this call has no arguments
printValue(6); // 6 is the argument passed to function printValue()
add(2, 3); // 2 and 3 are the arguments passed to function add()Zauważ, że wiele argumentów jest również oddzielanych przecinkami.
Jak parametry i argumenty współpracują ze sobą
Podczas wywoływania funkcji wszystkie parametry funkcji są tworzone jako zmienne, a wartość każdego z argumentów jest kopiowany do pasującego parametru (przy użyciu inicjalizacji kopiowania). Proces ten nazywa się przekazać wartość. Parametry funkcji wykorzystujące przekazywanie wartości nazywane są parametrami wartości.
Na przykład:
#include <iostream>
// This function has two integer parameters, one named x, and one named y
// The values of x and y are passed in by the caller
void printValues(int x, int y)
{
std::cout << x << '\n';
std::cout << y << '\n';
}
int main()
{
printValues(6, 7); // This function call has two arguments, 6 and 7
return 0;
}Gdy funkcja printValues jest wywoływana z argumentami 6 i 7, printValuesparametru x jest tworzony i inicjowany wartością 6, I printValuesparametru y jest tworzony i inicjowany wartością 7.
To daje wynik:
6 7
Zauważ, że liczba argumentów musi ogólnie odpowiadać liczbie parametrów funkcji, w przeciwnym razie kompilator zgłosi błąd. Argumentem przekazywanym do funkcji może być dowolne prawidłowe wyrażenie (ponieważ argument jest w zasadzie tylko inicjatorem parametru, a inicjatorami może być dowolne prawidłowe wyrażenie).
Naprawianie naszego programu wyzwań
Mamy teraz narzędzie potrzebne do naprawienia programu, który przedstawiliśmy na początku lekcji:
#include <iostream>
int getValueFromUser()
{
std::cout << "Enter an integer: ";
int input{};
std::cin >> input;
return input;
}
void printDouble(int value) // This function now has an integer parameter
{
std::cout << value << " doubled is: " << value * 2 << '\n';
}
int main()
{
int num { getValueFromUser() };
printDouble(num);
return 0;
}W tym programie zmienna num jest najpierw inicjowana wartością wprowadzoną przez użytkownika. Następnie wywoływana jest funkcja printDouble , a wartość argumentu num jest kopiowana do wartości parametru funkcji printDouble. Funkcja printDouble następnie wykorzystuje wartość parametru wartości.
Używając zwracanych wartości jako argumentów
W powyższym problemie widzimy, że zmienna num jest używana tylko raz, aby przenieść wartość zwracaną przez funkcję getValueFromUser do argumentu wywołania funkcja printDouble.
Możemy nieco uprościć powyższy przykład w następujący sposób:
#include <iostream>
int getValueFromUser()
{
std::cout << "Enter an integer: ";
int input{};
std::cin >> input;
return input;
}
void printDouble(int value)
{
std::cout << value << " doubled is: " << value * 2 << '\n';
}
int main()
{
printDouble(getValueFromUser());
return 0;
}Teraz używamy wartości zwracanej przez funkcję getValueFromUser bezpośrednio jako argumentu funkcji printDouble!
Chociaż ten program jest bardziej zwięzły (i sprawia, że jasne, że wartość odczytana przez użytkownika nie będzie używana do niczego innego), ta „zwarta składnia” może również okazać się nieco trudna do odczytania. Jeśli wolisz pozostać przy wersji, która zamiast tego używa zmiennej, nie ma problemu.
Jak parametry współpracują ze sobą i zwracane wartości
Używając zarówno parametrów, jak i wartości zwracanej, możemy utworzyć funkcje, które przyjmują dane jako dane wejściowe, wykonują na nich pewne obliczenia i zwracają wartość wywołującemu.
Oto przykład bardzo prostej funkcji, która dodaje dwie liczby do siebie i zwraca wynik do funkcji wywołujący:
#include <iostream>
// add() takes two integers as parameters, and returns the result of their sum
// The values of x and y are determined by the function that calls add()
int add(int x, int y)
{
return x + y;
}
// main takes no parameters
int main()
{
std::cout << add(4, 5) << '\n'; // Arguments 4 and 5 are passed to function add()
return 0;
}Wykonanie rozpoczyna się od góry głównego. Podczas oceny add(4, 5) wywoływana jest funkcja add , której parametr x jest inicjowany wartością 4, a parametr y jest inicjowany wartością 5.
Klasa oświadczenie zwrotne w funkcji add ocenia x + y w celu wygenerowania wartości 9, która jest następnie wrócił do głównego. Ta wartość 9 jest następnie wysyłana do std::cout w celu wydrukowania na konsoli.
Wyjście:
9
W formacie obrazkowym:

Więcej przykładów
Przyjrzyjmy się kilku innym wywołaniom funkcji:
#include <iostream>
int add(int x, int y)
{
return x + y;
}
int multiply(int z, int w)
{
return z * w;
}
int main()
{
std::cout << add(4, 5) << '\n'; // within add() x=4, y=5, so x+y=9
std::cout << add(1 + 2, 3 * 4) << '\n'; // within add() x=3, y=12, so x+y=15
int a{ 5 };
std::cout << add(a, a) << '\n'; // evaluates (5 + 5)
std::cout << add(1, multiply(2, 3)) << '\n'; // evaluates 1 + (2 * 3)
std::cout << add(1, add(2, 3)) << '\n'; // evaluates 1 + (2 + 3)
return 0;
}Ten program generuje wynik:
9 15 10 7 6
Pierwsza instrukcja jest prosta.
W drugiej instrukcji argumenty są wyrażenia, które są oceniane przed przekazaniem. W tym przypadku 1 + 2 wyniesie 3, więc 3 jest kopiowany do parametru x. 3 * 4 wyniesie 12, więc 12 jest kopiowany do parametru y. add(3, 12) rozwiązuje się do 15.
Następna para instrukcji jest również stosunkowo prosta:
int a{ 5 };
std::cout << add(a, a) << '\n'; // evaluates (5 + 5)W tym przypadku add() jest wywoływana, gdy wartość a jest kopiowana do obu parametry x i y. Ponieważ a ma wartość 5, add(a, a) = add(5, 5), co przekłada się na wartość 10.
Przyjrzyjmy się pierwszej trudnej instrukcji w grupie:
std::cout << add(1, multiply(2, 3)) << '\n'; // evaluates 1 + (2 * 3)Gdy funkcja add jest wykonywana, program musi określić, jakie wartości parametry x i y są. x jest proste, ponieważ właśnie przekazaliśmy mu liczbę całkowitą 1. Aby otrzymać wartość parametru y, należy najpierw obliczyć mnożenie(2, 3) . Program wywołuje mnożenie i inicjuje z = 2 i w = 3, więc mnożenie(2, 3) zwraca wartość całkowitą 6. Ta zwracana wartość 6 może być teraz użyta do inicjalizacji y parametru add . add(1, 6) zwraca liczbę całkowitą 7, która jest następnie przekazywana do std::cout w celu wydrukowania.
Put less szczegółowo:
add(1, multiply(2, 3)) wyniesie add(1, 6) wyniesie 7
Poniższa instrukcja wygląda na skomplikowaną, ponieważ jeden z argumentów podanych do add jest kolejnym wywołaniem add.
std::cout << add(1, add(2, 3)) << '\n'; // evaluates 1 + (2 + 3)Ale w tym przypadku działa dokładnie tak samo jak poprzedni przypadek. add(2, 3) jest rozwiązywane jako pierwsze, co daje wartość zwracaną 5. Teraz może rozwiązać funkcję add(1, 5), której wynikiem jest wartość 6, która jest przekazywana do std::cout w celu wydrukowania.
Mniej szczegółowo:
add(1, add(2, 3)) wyniesie add(1, 5) => ocenia do 6
Parametry bez odniesień i parametry bez nazw
W niektórych przypadkach można spotkać funkcje posiadające parametry, które nie są używane w treści funkcji. Są to tak zwane parametry bez odniesień.
Jako trywialny przykład:
void doSomething(int count) // warning: unreferenced parameter count
{
// This function used to do something with count but it is not used any longer
}
int main()
{
doSomething(4);
return 0;
}Podobnie jak w przypadku nieużywanych zmiennych lokalnych, Twój kompilator prawdopodobnie ostrzeże, że zmienna count została zdefiniowana, ale nie została użyta.
W definicji funkcji nazwa parametru funkcji jest opcjonalna. Dlatego w przypadkach, gdy parametr funkcji musi istnieć, ale nie jest używany w treści funkcji, można po prostu pominąć nazwę. Parametr bez nazwy nazywany jest parametrem nienazwanym:
void doSomething(int) // ok: unnamed parameter will not generate warning
{
}Przewodnik po stylu Google C++ zaleca użycie komentarza w celu udokumentowania, czym był parametr bez nazwy:
void doSomething(int /*count*/)
{
}Nota autora
Prawdopodobnie zastanawiasz się, po co pisać funkcję zawierającą parametr, którego wartość nie jest używana. Dzieje się tak najczęściej w przypadkach podobnych do poniższych:
- Załóżmy, że mamy funkcję z jednym parametrem. Później funkcja jest w jakiś sposób aktualizowana i wartość parametru nie jest już potrzebna. Gdyby po prostu usunięto nieużywany teraz parametr funkcji, każde istniejące wywołanie funkcji zostałoby przerwane (ponieważ wywołanie funkcji dostarczałoby więcej argumentów, niż funkcja jest w stanie przyjąć). Wymagałoby to znalezienia każdego wywołania funkcji i usunięcia niepotrzebnego argumentu. Może to wymagać dużo pracy (i wymagać wielu ponownych testów). Może to być nawet niemożliwe (w przypadkach, gdy nie kontrolowaliśmy całego kodu wywołującego funkcję). Zamiast tego możemy pozostawić parametr bez zmian i po prostu pozwolić mu nic nie robić.
Dla zaawansowanych czytelników
Inne przypadki, w których to nastąpi:
- Operatorzy
++i--mają warianty przedrostka i postfiksu (np.++foovsfoo++). Parametr funkcji bez odniesienia służy do rozróżnienia, czy przeciążenie takiego operatora dotyczy przypadku przedrostka czy przyrostka. Omówiliśmy to w lekcji 21.8 — Przeciążanie operatorów inkrementacji i dekrementacji. - Kiedy musimy określić coś na podstawie typu (a nie wartości) parametru szablonu typu.
Nota autora
Jeśli nienazwane parametry nadal nie mają dla Ciebie sensu, nie martw się. Spotkamy się z nimi ponownie na przyszłych lekcjach, gdy będziemy mieli więcej kontekstu do wyjaśnienia, kiedy są przydatne.
Najlepsza praktyka
Jeśli parametr funkcji istnieje, ale nie jest używany w treści funkcji, nie nadawaj mu nazwy. Opcjonalnie możesz umieścić nazwę w komentarzu.
Wnioski
Parametry funkcji i zwracane wartości to kluczowe mechanizmy, dzięki którym można pisać funkcje w sposób umożliwiający ich wielokrotne użycie, ponieważ pozwala nam to pisać funkcje, które mogą wykonywać zadania i zwracać pobrane lub obliczone wyniki z powrotem do wywołującego, nie wiedząc wcześniej, jakie są konkretne dane wejściowe lub wyjściowe.
Czas quizu
Pytanie nr 1
Co jest nie tak z tym fragmentem programu?
#include <iostream>
void multiply(int x, int y)
{
return x * y;
}
int main()
{
std::cout << multiply(4, 5) << '\n';
return 0;
}Pytanie nr 2
Jakie dwie rzeczy są nie tak z tym fragmentem programu?
#include <iostream>
int multiply(int x, int y)
{
int product { x * y };
}
int main()
{
std::cout << multiply(4) << '\n';
return 0;
}Pytanie nr 3
Jaką wartość wypisuje następujący program?
#include <iostream>
int add(int x, int y, int z)
{
return x + y + z;
}
int multiply(int x, int y)
{
return x * y;
}
int main()
{
std::cout << multiply(add(1, 2, 3), 4) << '\n';
return 0;
}Pytanie nr 4
Napisz funkcję o nazwie doubleNumber(), która przyjmuje jeden parametr będący liczbą całkowitą. Funkcja powinna zwrócić podwójną wartość parametru.
Pytanie #5
- Napisz kompletny program, który odczyta liczbę całkowitą od użytkownika, podwoi ją za pomocą funkcji doubleNumber(), którą napisałeś w poprzednim pytaniu quizu, a następnie wypisuje podwojoną wartość na konsoli.

