Процедуры, функции.
Тернарный оператор.
Если вам понадобилось написать очень простое условие, то бывает полезно воспользоваться тернарным оператором.
#include <iostream>
using namespace std;
int main()
{
int a, b, max, min;
cin >> a >> b;
// используя тернарный оператор, определяем максимум
max = (a > b) ? a : b; // и сразу записываем его в переменную max
if (a > b) {
max = a;
} else {
max = b;
}
min = (a < b) ? a : b; // так же определяем и записываем min
if (a < b) {
min = a;
} else {
min = b;
}
(a == max) ? (cout << "The first number is bigger") : (cout << "The second number is bigger");
}
Как вы видите с помощью тернарника 5 строк кода волшебным образом превращаются в одну, поэтому если ваше условие состоит только в том, чтобы изменить в конечном итоге одну переменную или же выполнить одно действие, то использование тернарного оператора очень эффективно.
Что такое функция. Пример.
Примеры функций на Clojure:
(def mem-fib
(memoize
(fn [n]
(cond
(== 0 n) 1
(== 1 n) 1
:else (+ (mem-fib (- n 1)) (mem-fib (- n 2)))))))
Если все-таки отойти от таких извращений как функциональные языки программирования и вернуться в С++, то все станет не так страшно и ужасно…(нет)
Несколько правил для безболезненного написания функций (рекомендуем проверять все эти пункты каждый раз после написания функции):
-
Любая функция имеет тип, также, как и любая переменная.
-
Функция может возвращать значение, тип которого в большинстве случаев аналогичен типу самой функции.
-
Если функция не возвращает никакого значения, то она должна иметь тип void (такие функции иногда называют процедурами)
-
При объявлении функции, после ее типа должно находиться имя функции и две круглые скобки — открывающая и закрывающая, внутри которых могут находиться один или несколько аргументов функции, которых также может не быть вообще.
-
После списка аргументов функции ставится открывающая фигурная скобка, после которой находится само тело функции.
-
В конце тела функции обязательно ставится закрывающая фигурная скобка.
#include <iostream> using namespace std; void sayHello() { cout << "Hello, world\n"; } int miltiply(int a, int b) { return a * b; } int pow(int n, int k) { int tmp = 1; for (int i = 0; i < k; ++i) { tmp = multiply(tmp, n); // tmp *= n; } return tmp; } int main() { sayHello(); int a = 5, k = 6; cout << "2 в степени 5: " << pow(2, 5) << '\n'; cout << "5 в степени 6: " << pow(a, k) << '\n'; }
Локальные и глобальные переменные.
Переменные в С++ бывают локальными и глобальными. В примере ниже переменная int global
является глобальной, а
переменная int local
является локальной.
Мы очень настоятельно просим вас не использовать глобальные переменные. Объясняем, почему.
Каждые { }
в С++ определяют свою область видимости переменной, поэтому, если вы создаете переменные с одинаковым
названием, находящиеся в непересекающихся областях видимости, то у вас никогда не возникнет проблем с тем, что одна из
переменных случайно “перекроется” другой, как это происходит ниже при создании внутри функции foo
одноименной
переменной, с глобальной переменной global
.
Важно запомнить, что приоритете при обращении отдается переменной с наименьшей областью видимости!!!
#include <iostream>
using namespace std;
int global = 0;
int foo(int a, int b) {
int global = a + b;
return global;
}
int main() {
int local = boo(2, 5);
cout << (global == local ? "EQUAL" : "DIFFERENT"); // DIFFERENT
}
Замечание. То же происходит, когда вы объявляете случайно одноименную переменную внутри уже определенного цикла, условия и тд.
Формальные и фактические параметры.
В примерах выше были показаны функции с передачей различных параметров. Давайте разберемся.
int increment(int a)
{
return ++a;
}
int main()
{
int a = 0, b = 0;
cout << a << increment(a) << a; //
cout << b << increment(b) << b; // В чем разница?
}
Передача аргументов в функцию будет происходить по значению. При вызове функции внутри нее будет создаваться ещё одна переменная, в которую будет копироваться значение аргумента. В данном случае изменение переменной внутри функции не влечет за собой изменений переменной аргумента (обратная ситуация будет рассмотрена позже).
Иногда очень удобно иметь возможность вызывать функцию как с передачей аргумента, так и без. Каким образом можно это реализовать?
Вариант первый - установка значений аргумента “по умолчанию”.
void greet (string name = "user")
{
cout << "Greetings, " + name;
}
int main()
{
greet();
greet("Fellow gamer");
}
Отличия функций от процедур.
В примере с функциями был использован оператор return
. Это ключевое слово останавливает выполнение функции и
подставляет в место её вызова некоторое значение.
ВАЖНО: типа возвращаемого значения должен совпадать с “типом“ функции
int foo ()
{
return "ATATA, нельзя так";
}
Если функция имеет тип void
, то она называется процедурой.
int sum (int a, int b)
{
return a + b;
cout << "Эта строка никогда не выведется";
}
int main()
{
cout << "Сюда будет подставлено значение функции: " << sum(2, 2);
}
Перегрузка
Вспомните вопрос про вызов функции с разным набором аргументов.
Вариант второй - перегрузить функцию.
void greet (string name = "user")
{
cout << "Greetings, " + name;
}
void greet ()
{
cout << "Greetings, user";
}
Компилятор сам понимает, какая из функций будет вызвана по количеству аргументов и их типу.
void foo (int a);
void foo (short b);
int main ()
{
foo (0); // Будет вызвана первая функция
}
Если точная подстановка не удалась, то компилятор попытается найти совпадение путем неявного преобразования типов.
- char, unsigned char и short конвертируются в int;
- unsigned short может конвертироваться в int или unsigned int (в зависимости от размера int);
- float конвертируется в double;
Если таких преобразований нет, то С++ попробует преобразовать каждый числовой тип к необходимому.
Что произойдет, если компилятор найдет несколько совпадений?
void foo (unsigned int value);
void foo (float value);
int main()
{
foo('b'); // Это char
foo(0); // Это int
foo(3.14159); // Это double
}