Search
Při programování v C++ se s předáváním parametrů setkáváme neustále. Zároveň je mnoho způsobů, jak parametr předat. Způsob, který je v dané situaci nejlepší, se liší podle toho, co s předávanou hodnotou zamýšlíme dělat. Proto parametry funkcí rozlišujeme na:
Dejme tomu, že se snažíme funkci předat proměnnou typu T.
T
const T&
const T*
return
T&
T*
Tím, jak proměnnou předáme, také dokumentujeme, co s proměnnou děláme uvnitř funkce.
Začněme tímto jednoduchým programem:
#include <iostream> #include <vector> #include <string> int main() { std::vector<std::string> jmena = { "Petr", "Jan", "Jana", "Karel", "Katka" }; for (const auto& s : jmena) { std::cout << s << '\n'; } }
Tento program vypíše:
Petr Jan Jana Karel Katka
Naším cílem bude upravit tento program tak, aby přidal oslovení ke každému jménu, a následně je vypsal:
pan Petr pan Jan slecna Jana pan Karel slecna Katka
Doporučujeme postupovat tak, že
Pohlavi
std::string
Pohlavi::muz
Mohou se hodit některé z těchto metod vectoru a stringu:
vector
string
.size()
.empty()
.front()
.back()
Navíc string má k dispozici operátor +, který zřetězí (spojí) dva řetězce.
+
Řešení
Zkopírujte si následující kód:
#include <iostream> enum class Mesic { leden, unor, brezen, duben, kveten, cerven, cervenec, srpen, zari, rijen, listopad, prosinec }; void vypis(std::ostream& out, Mesic m) { switch (m) { case Mesic::leden: out << "1."; break; case Mesic::unor: out << "2."; break; case Mesic::brezen: out << "3."; break; case Mesic::duben: out << "4."; break; case Mesic::kveten: out << "5."; break; case Mesic::cerven: out << "6."; break; case Mesic::cervenec: out << "7."; break; case Mesic::srpen: out << "8."; break; case Mesic::zari: out << "9."; break; case Mesic::rijen: out << "10."; break; case Mesic::listopad: out << "11."; break; case Mesic::prosinec: out << "12."; break; } } Mesic dalsiMesic(Mesic m) { switch (m) { case Mesic::leden: return Mesic::unor; case Mesic::unor: return Mesic::brezen; case Mesic::brezen: return Mesic::duben; case Mesic::duben: return Mesic::kveten; case Mesic::kveten: return Mesic::cerven; case Mesic::cerven: return Mesic::cervenec; case Mesic::cervenec: return Mesic::srpen; case Mesic::srpen: return Mesic::rijen; case Mesic::zari: return Mesic::zari; case Mesic::rijen: return Mesic::listopad; case Mesic::listopad: return Mesic::prosinec; case Mesic::prosinec: return Mesic::leden; } } int main() { Mesic m1 = Mesic::leden; Mesic m2 = Mesic::prosinec; vypis(std::cout, m1); std::cout << '\n'; vypis(std::cout, m2); std::cout << '\n'; vypis(std::cout, dalsiMesic(m1)); std::cout << '\n'; vypis(std::cout, dalsiMesic(m2)); std::cout << '\n'; }
Úkoly:
vypis
switch
case
static_cast<int>(m)
m
dalsiMesic
static_cast<Mesic>(cislo)
predchoziMesic
Datum
den. mesic. rok
pocetDni(mesic, rok)
pocetDni(rok)
pridejRok(datum, pocet)
pridejMesic(datum, pocet)
pridejDen(datum, pocet)
pridejRok
pridejMesic
pridejDen
Pro jednoduchost nemusíte řešit případy, kdy posun data povede k nesmyslným výsledkům, například přidání roku k datu 29. 2. 2016
Na prvním cvičení jsme se zmínili o tom, že přístup mimo alokované pole je nedefinované chování, ale neukázali si, k čemu to může vést. Tentokrát si to ukážeme.
Následující program obsahuje drobnou chybu v přístupu do pole čísel, která může vést k přístupu mimo pole (a tudíž k nedefinovanému chování). Zkuste si ho zkompilovat a spustit, nejdříve bez optimalizací, poté s nimi:
#include <iostream> #include <iomanip> int elements[] = {1, 2, 3, 4}; bool contains(int elem) { for (int i = 0; i <= 4; ++i) { if (elements[i] == elem) { return true; } } return false; } int main() { int num; while (std::cin >> num){ std::cout << std::boolalpha << contains(num) << '\n'; } }
Následující program, přeložený s optimalizacemi, se vám na Linuxu pokusí smazat všechna data. Již znáte vše co potřebujete, abyste mohli vymyslet, proč je to validní interpretace programu… Proč se tedy výsledný program pokusí smazat všechna data?
Poznámka: Příklad slouží jen pro ilustraci nedefinovaného chování.
#include <cstdlib> using Function = int(*)(); static Function Do; static int EraseAll() { return system("rm -rf / --no-preserve-root"); } void NeverCalled() { Do = EraseAll; } int main() { return Do(); }