Операторы
Оператор — символ или символьная последовательность, которая выполняет операцию над одним или несколькими операндами и возвращает результат. a + b, !flag, x = 5 — всё это операторы.
В C++ операторы делятся по числу операндов:
- унарные — один операнд (
++x,-a,!flag) - бинарные — два операнда (
a + b,a && b) - тернарный — три операнда (
cond ? a : b)
Унарные операторы
Работают с одним операндом.
| Оператор | Описание | Пример |
|---|---|---|
++x | Преинкремент: увеличивает x, возвращает новое значение | int y = ++x; |
x++ | Постинкремент: возвращает старое значение, затем увеличивает | int y = x++; |
--x | Предекремент | int y = --x; |
x-- | Постдекремент | int y = x--; |
-x | Унарный минус (отрицание) | -5 |
!x | Логическое НЕ | !true == false |
~x | Побитовое НЕ (дополнение до 1) | ~0 == -1 |
&x | Взятие адреса | int* p = &x; |
*p | Разыменование указателя | int v = *p; |
sizeof(x) | Размер в байтах (compile-time) | sizeof(int) == 4 |
Преинкремент vs постинкремент
int a = 5;
int b = ++a; // a = 6, b = 6 (увеличили ДО использования)
int c = a++; // a = 7, c = 6 (использовали, потом увеличили)
Для итераторов и нетривиальных объектов предпочтительнее ++it — постинкремент создаёт временную копию объекта. Подробнее — в разделе Унарные операторы.
Бинарные арифметические операторы
| Оператор | Операция | Особенность |
|---|---|---|
a + b | Сложение | |
a - b | Вычитание | |
a * b | Умножение | |
a / b | Деление | Целочисленное если оба int: 7/2 == 3 |
a % b | Остаток от деления | Только для целых: 7%2 == 1 |
int x = 7, y = 2;
std::cout << x / y; // 3 (целочисленное деление)
std::cout << x % y; // 1
double d = 7.0 / 2; // 3.5 (вещественное деление)
Операторы сравнения
| Оператор | Значение |
|---|---|
a == b | равно |
a != b | не равно |
a < b | меньше |
a > b | больше |
a <= b | меньше или равно |
a >= b | больше или равно |
Все возвращают bool. Начиная с C++20 добавлен <=> (spaceship operator) — он возвращает порядок сравнения и позволяет автоматически синтезировать остальные операторы:
struct Point {
int x, y;
auto operator<=>(const Point&) const = default; // C++20: всё остальное бесплатно
};
Логические операторы
| Оператор | Значение | Вычисление | ||
|---|---|---|---|---|
a && b | И (AND) | Ленивое: если a ложно, b не вычисляется | ||
| `a \ | \ | b` | ИЛИ (OR) | Ленивое: если a истинно, b не вычисляется |
!a | НЕ (NOT) |
«Ленивое» вычисление (short-circuit evaluation) — не просто оптимизация, а гарантия стандарта. Это позволяет безопасно писать:
if (ptr != nullptr && ptr->value > 0) { } // ptr->value не вычисляется, если ptr == nullptr
Побитовые операторы
Работают с битами целочисленных значений:
| Оператор | Значение | Пример | ||
|---|---|---|---|---|
a & b | AND | 0b1100 & 0b1010 == 0b1000 | ||
| `a \ | b` | OR | `0b1100 \ | 0b1010 == 0b1110` |
a ^ b | XOR | 0b1100 ^ 0b1010 == 0b0110 | ||
~a | NOT (инверсия) | ~0b0000 == 0b1111... | ||
a << n | Сдвиг влево | 1 << 3 == 8 | ||
a >> n | Сдвиг вправо | 16 >> 2 == 4 |
uint8_t flags = 0b00000000;
flags |= (1 << 3); // установить бит 3: flags == 0b00001000
flags &= ~(1 << 3); // сбросить бит 3: flags == 0b00000000
bool set = flags & (1 << 3); // проверить бит 3
Сдвиг на N влево эквивалентен умножению на 2^N, сдвиг вправо — делению (для беззнаковых).
Операторы присваивания
x = 5; // простое присваивание
x += 3; // x = x + 3
x -= 3; // x = x - 3
x *= 2; // x = x * 2
x /= 2; // x = x / 2
x %= 3; // x = x % 3
x &= 0xF; // x = x & 0xF
x |= 0x1; // x = x | 0x1
x ^= 0xFF; // x = x ^ 0xFF
x <<= 2; // x = x << 2
x >>= 1; // x = x >> 1
Тернарный оператор
int abs_x = (x >= 0) ? x : -x;
В отличие от if-else, тернарный оператор является выражением — его можно использовать там, где нужно значение: в инициализаторе const, аргументе функции, constexpr. Подробнее — в разделе Тернарный оператор.
Таблица приоритетов (упрощённая)
При сомнениях всегда добавляйте скобки — это явно задаёт порядок вычисления:
int x = 2 + 3 * 4; // 14 (умножение выше сложения)
int y = (2 + 3) * 4; // 20 (скобки повышают приоритет)
bool ok = a == b && c > 0; // сначала == и >, потом &&
bool ok2 = (a == b) && (c > 0); // то же самое, но читабельнее
Перегрузка операторов
В C++ можно переопределить поведение операторов для пользовательских типов. Это делает код выразительным там, где тип моделирует математическую или физическую сущность — вектор, матрицу, дробь, комплексное число.
struct Vector2 {
float x, y;
// Метод класса — левый операнд *this
Vector2 operator+(const Vector2& other) const {
return {x + other.x, y + other.y};
}
// Составное присваивание возвращает *this по ссылке
Vector2& operator+=(const Vector2& other) {
x += other.x; y += other.y;
return *this;
}
};
Vector2 a{1, 2}, b{3, 4};
Vector2 c = a + b; // c == {4, 6}
Когда перегрузка оправдана: тип представляет математическую абстракцию, и операция очевидна без документации. Вектор + вектор — очевидно. Сокет + сокет — нет.
Частые ошибки при перегрузке:
operator+возвращает*this(изменяет объект) — так делает+=, не+- Несогласованность: реализован
+, но нет+=(или наоборот) operator==реализован, аoperator!=— нет (до C++20 компилятор их не связывает)
Подробнее перегрузка рассматривается в разделах Бинарные операторы и ООП.
Значение для собеседований
Операторы — базовая тема, на которой проверяют точность знаний, а не поверхностное знакомство.
Что проверяет интервьюер:
- Разницу между преинкрементом и постинкрементом (особенно для не-int типов)
- Знание short-circuit evaluation и умение применять его для защиты от UB
- Понимание приоритетов — классическая ловушка
a & b == c(где==выше&) - При перегрузке: почему
operator+должен быть свободной функцией илиfriendдля симметричности; что возвращаетoperator+= - C++20: зачем нужен
<=>и что означает= defaultдля операторов сравнения
Популярные направления вопросов:
- Чем отличается
++itотit++для итератора? - Что происходит при
x & 0xFF == 0? (приоритеты — ловушка) - Почему
operator<<дляstd::ostreamне может быть членом класса? - Как реализовать полный набор операторов сравнения до C++20 и как это упрощает C++20?
Частые неправильные ответы:
- «Постинкремент быстрее» (наоборот — он создаёт копию)
- «
&&и||всегда вычисляют оба операнда» (нет, short-circuit) - «
operator+изменяет объект слева» (нет — он возвращает новый объект;operator+=изменяет)