Меню
Главная
Случайная статья
Настройки
|
Язык программирования C++ поддерживает все операторы своего прародителя Си и дополнен новыми операторами и возможностями.
После вычисления первого операнда для неперегруженных операторов «&&», «||» и «,» (оператор «запятая», англ. comma) компилятор вставляет точку следования (англ. sequence point), гарантирующую, что все побочные эффекты (например, оператор «постфиксный ++») будут выполнены до начала вычисления второго операнда.
Языки с Си-подобным синтаксисом (например, Java, C#, PHP и другие) часто заимствуют операторы Cи/C++ с сохранением не только поведения, но также приоритета и ассоциативности.
Содержание
Таблицы
В таблицах используются следующие обозначения:
- «a», «b» и «c»: имена объектов или значения (литералы, значения переменных, возвращаемые значения, lvalue);
- «Перегружаемый»: возможность перегрузки оператора в языке C++;
- «Реализован в Си»: существование оператора в языке Си;
- «R», «T», «S»: имена типов;
- «Пример»: пример объявления перегруженного оператора;
- «Член типа T»: определение оператора в виде метода структуры или класса (внутри структуры или класса); пример:
struct T { // или class
operator float () const;
};
T::operator float () const { /* реализация */ };
- «Определение вне класса»: определение оператора в виде функции; пример:
#include <iostream>
struct T { // или class
/* ... */
};
std::ostream & operator << ( std::ostream & a, T const & b ) { /* реализация */ }
Арифметические операторы
Операция (выражение)
|
Оператор
|
Синтаксис выражения
|
Перегружаемый
|
Реализован в Си
|
Пример
|
Член типа T
|
Определение вне класса
|
Присваивание
|
= |
a = b |
Да |
Да |
R& T::operator =(S b);
|
н/д
|
Сложение
|
+ |
a + b |
Да |
Да |
R T::operator +(S b);
|
R operator +(T a, S b);
|
Вычитание
|
- |
a - b |
Да |
Да |
R T::operator -(S b);
|
R operator -(T a, S b);
|
Унарный плюс
|
+ |
+a |
Да |
Да |
R T::operator +();
|
R operator +(T a);
|
Унарный минус
|
- |
-a |
Да |
Да |
R T::operator -();
|
R operator -(T a);
|
Умножение
|
* |
a * b |
Да |
Да |
R T::operator *(S b);
|
R operator *(T a, S b);
|
Деление
|
/ |
a / b |
Да |
Да |
R T::operator /(S b);
|
R operator /(T a, S b);
|
Операция модуль (остаток от деления целых чисел)[note 1]
|
% |
a % b |
Да |
Да
|
R T::operator %(S b);
|
R operator %(T a, S b);
|
Инкремент
|
префиксный
|
++
|
++a
|
Да
|
Да
|
R& T::operator ++();
|
R& operator ++(T a);
|
суффиксный (постфиксный)
|
++
|
a++
|
Да
|
Да
|
R T::operator ++(int);
|
R operator ++(T a, int);
|
[note 2]
|
Декремент
|
префиксный
|
--
|
--a |
Да |
Да
|
R& T::operator --();
|
R& operator --(T a);
|
суффиксный (постфиксный)
|
--
|
a-- |
Да |
Да
|
R T::operator --(int);
|
R operator --(T a, int);
|
[note 2]
|
Операторы сравнения
Операция (выражение)
|
Оператор
|
Синтаксис выражения
|
Перегружаемый
|
Реализован в Си
|
Пример
|
Член типа T
|
Определение вне класса
|
Сравнение
|
== |
a == b
|
Да
|
Да
|
R T::operator ==(S b);
|
R operator ==(T a, S b);
|
Неравенство
|
!= |
a != b |
Да |
Да
|
R T::operator !=(S b);
|
R operator !=(T a, S b);
|
Больше
|
> |
a > b |
Да |
Да
|
R T::operator >(S b);
|
R operator >(T a, S b);
|
Меньше
|
< |
a < b |
Да |
Да
|
R T::operator <(S b);
|
R operator <(T a, S b);
|
Больше или равно
|
>= |
a >= b |
Да |
Да
|
R T::operator >=(S b);
|
R operator >=(T a, S b);
|
Меньше или равно
|
<= |
a <= b |
Да |
Да
|
R T::operator <=(S b);
|
R operator <=(T a, S b);
|
Логические операторы
Операция (выражение)
|
Оператор
|
Синтаксис выражения
|
Перегружаемый
|
Реализован в Си
|
Пример
|
Член типа T
|
Определение вне класса
|
Логическое отрицание, НЕ
|
! |
!a
|
Да
|
Да
|
R T::operator !();
|
R operator !(T a);
|
Логическое умножение, И
|
&& |
a && b |
Да |
Да
|
R T::operator &&(S b);
|
R operator &&(T a, S b);
|
Логическое сложение, ИЛИ
|
|| |
a || b |
Да |
Да
|
R T::operator ||(S b);
|
R operator ||(T a, S b);
|
Побитовые операторы
Операция (выражение)
|
Оператор
|
Синтаксис выражения
|
Перегружаемый
|
Реализован в Си
|
Пример
|
Член типа T
|
Определение вне класса
|
Побитовая инверсия
|
~ |
~a
|
Да
|
Да
|
R T::operator ~();
|
R operator ~(T a);
|
Побитовое И
|
& |
a & b |
Да |
Да
|
R T::operator &(S b);
|
R operator &(T a, S b);
|
Побитовое ИЛИ (or)
|
| |
a | b |
Да |
Да
|
R T::operator |(S b);
|
R operator |(T a, S b);
|
Побитовое исключающее ИЛИ (xor)
|
^ |
a ^ b |
Да |
Да
|
R T::operator ^(S b);
|
R operator ^(T a, S b);
|
Побитовый сдвиг влево[note 3]
|
<< |
a << b |
Да |
Да
|
R T::operator <<(S b);
|
R operator <<(T a, S b);
|
Побитовый сдвиг вправо[note 3][note 4]
|
>> |
a >> b |
Да |
Да
|
R T::operator >>(S b);
|
R operator >>(T a, S b);
|
Составное присваивание
Операция (выражение)
|
Оператор
|
Синтаксис выражения
|
Значение
|
Перегружаемый
|
Реализован в Си
|
Пример
|
Член типа T
|
Определение вне класса
|
Сложение, совмещённое с присваиванием
|
+= |
a += b
|
a = a + b
|
Да
|
Да
|
R T::operator +=(S b);
|
R operator +=(T a, S b);
|
Вычитание, совмещённое с присваиванием
|
-= |
a -= b
|
a = a - b
|
Да
|
Да
|
R T::operator -=(S b);
|
R operator -=(T a, S b);
|
Умножение, совмещённое с присваиванием
|
*= |
a *= b
|
a = a * b
|
Да
|
Да
|
R T::operator *=(S b);
|
R operator *=(T a, S b);
|
Деление, совмещённое с присваиванием
|
/= |
a /= b
|
a = a / b
|
Да
|
Да
|
R T::operator /=(S b);
|
R operator /=(T a, S b);
|
Вычисление остатка от деления, совмещённое с присваиванием[note 1]
|
%= |
a %= b
|
a = a % b
|
Да
|
Да
|
R T::operator %=(S b);
|
R operator %=(T a, S b);
|
Побитовое «И» (AND), совмещённое с присваиванием
|
&= |
a &= b
|
a = a & b
|
Да
|
Да
|
R T::operator &=(S b);
|
R operator &=(T a, S b);
|
Побитовое «ИЛИ» (or), совмещённое с присваиванием
|
|= |
a |= b
|
a = a | b
|
Да
|
Да
|
R T::operator |=(S b);
|
R operator |=(T a, S b);
|
Побитовое «исключающее ИЛИ» (xor), совмещённое с присваиванием
|
^= |
a ^= b
|
a = a ^ b
|
Да
|
Да
|
R T::operator ^=(S b);
|
R operator ^=(T a, S b);
|
Побитовый сдвиг влево, совмещённый с присваиванием
|
<<= |
a <<= b
|
a = a << b
|
Да
|
Да
|
R T::operator <<=(S b);
|
R operator <<=(T a, S b);
|
Побитовый сдвиг вправо, совмещённый с присваиванием[note 4]
|
>>= |
a >>= b
|
a = a >> b
|
Да
|
Да
|
R T::operator >>=(S b);
|
R operator >>=(T a, S b);
|
Операторы работы с указателями и членами класса
Оператор
|
Синтаксис
|
Перегружаемый
|
Реализован в Си
|
Пример
|
Член типа T
|
Определение вне класса
|
Обращение к элементу массива
|
a[b]
|
Да
|
Да
|
R T::operator [](S b);
|
н/д
|
Непрямое обращение («объект, на который указывает a»)
|
*a |
Да |
Да
|
R T::operator *();
|
R operator *(T a);
|
Ссылка («адрес a»)
|
&a |
Да |
Да
|
R T::operator &();
|
R operator &(T a);
|
Обращение к члену структуры («член b объекта, на который указывает a»)
|
a->b |
Да |
Да
|
R* T::operator ->(); [note 5]
|
н/д
|
Обращение к члену структуры («член b объекта a»)
|
a.b |
Нет |
Да
|
н/д
|
Член, на который указывает b в объекте, на который указывает a[note 6]
|
a->*b |
Да |
Нет
|
R T::operator ->*(S b);
|
R operator ->*(T a, S b);
|
Член, на который указывает b в объекте a
|
a.*b |
Нет |
Нет
|
н/д
|
Другие операторы
Оператор
|
Синтаксис
|
Перегружаемый
|
Реализован в Си
|
Пример
|
Член типа T
|
Определение вне класса
|
Функтор
|
a(a1, a2)
|
Да
|
Да
|
R T::operator ()(S a1, U a2, ...);
|
н/д
|
Оператор «запятая»
|
a, b |
Да |
Да
|
R T::operator ,(S b);
|
R operator ,(T a, S b);
|
Тернарная условная операция
|
a ? b : c |
Нет |
Да
|
н/д
|
Оператор расширения области видимости
|
a::b |
Нет |
Нет
|
н/д
|
Пользовательские литералы (введены в C++11)
|
"a"_b |
Да |
Нет
|
н/д
|
R operator "" _b(T a)
|
Sizeof (размер)
|
sizeof(a) [note 7]
sizeof(type) |
Нет |
Да
|
н/д
|
Align-of (выравнивание)
|
alignof(type) или _Alignof(type) [note 8] |
Нет |
Да
|
н/д
|
Интроспекция
|
typeid(a)
typeid(type) |
Нет |
Нет
|
н/д
|
Приведение типа
|
(type) a |
Да |
Да
|
T::operator R();
|
н/д
|
[note 9]
|
Выделение памяти
|
new type |
Да |
Нет
|
void* T::operator new(size_t x);
|
void* operator new(size_t x);
|
Выделение памяти для массива
|
new type[n] |
Да |
Нет
|
void* T::operator new[](size_t x);
|
void* operator new[](size_t x);
|
Освобождение памяти
|
delete a |
Да |
Нет
|
void T::operator delete(void* x);
|
void operator delete(void* x);
|
Освобождение памяти, занятой массивом
|
delete[] a |
Да |
Нет
|
void T::operator delete[](void* x);
|
void operator delete[](void* x);
|
Примечания:
- 1 2 Оператор «%» работает только с целыми числами. Для чисел с плавающей точкой используйте функцию
fmod () из файла «math.h».
- 1 2 Чтобы отличить префиксный и суффиксный (постфиксный) операторы друг от друга, у постфиксных операторов добавлен неиспользуемый формальный параметр типа
int . Часто этому параметру даже не дают имя.
- 1 2 В библиотеке «iostream» операторы «
<< » и «>> » используются для работы с потоковым выводом и вводом.
- 1 2 По стандарту C99, сдвиг вправо отрицательного числа — implementation defined behavior (см. неуточняемое поведение). Многие компиляторы, в том числе gcc (см. документацию Архивная копия от 22 сентября 2019 на Wayback Machine (англ.)), реализуют арифметический сдвиг, но стандарт не запрещает реализовывать логический сдвиг.
- Тип возвращаемого значения оператора «
operator->() » должен быть типом, к которому применим оператор «-> », например, указателем. Если «x » имеет тип «C », и класс «C » перегружает оператор «operator->() », выражение «x->y » раскрывается как «x.operator->()->y ».
- См. пример в статье Архивная копия от 17 мая 2013 на Wayback Machine (англ.) «Реализация оператора
->* для умных указателей» Скотта Майерса из журнала «Dr. Dobb’s journal», выпуск за октябрь 1999 года.
- Оператор
sizeof , обычно, записывают со скобками. Если операнд — имя переменной, указание скобок необязательно. Если операнд — имя типа, скобки обязательны.
- Стандарт языка C++ определяет оператор
alignof . Аналогичный оператор в стандарте языка Си называется _Alignof .
- Для оператора приведения типа тип возвращаемого значения явно не указывается, так как совпадает с именем оператора.
Приоритеты операторов
В данной таблице указаны приоритеты операторов и их ассоциативность. Операторы, указанные в таблице выше (раньше), имеют более высокий приоритет (приоритет вычисления). При рассмотрении выражения, операторы, имеющие более высокий приоритет, будут вычислены раньше операторов с низким приоритетом. Если несколько операторов указаны в одной ячейке, то они имеют одинаковый приоритет и вычисляются в последовательности, задаваемой ассоциативностью. Приоритеты операторов не изменяются при их перегрузке.
Этой таблицы приоритетов в большинстве случаев бывает достаточно, за исключением следующих случаев. Тернарный оператор «?:» может содержать в среднем выражении оператор «запятая» или присваивание, но код «a ? b, c : d » компилятор воспринимает как «a ? (b, c) : d », а не как бессмысленное выражение «(a ? b), (c : d) ». Таким образом выражение между ? и : воспринимается, как если бы оно было в скобках.
Приоритет
|
Оператор
|
Описание
|
Ассоциативность
|
1
Наивысший
|
::
|
Разрешение области видимости
|
Нет
|
2
|
++
|
Суффиксный инкремент
|
Слева направо
|
--
|
Суффиксный декремент
|
()
|
Вызов функции
|
[]
|
Взятие элемента массива
|
.
|
Выбор элемента по ссылке
|
->
|
Выбор элемента по указателю
|
typeid()
|
RTTI (только C++; см typeid)
|
const_cast
|
Приведение типа (C++) (см const cast)
|
dynamic_cast
|
Приведение типа (C++) (см dynamic cast)
|
reinterpret_cast
|
Каламбур типизации (C++) (см reinterpret_cast)
|
static_cast
|
Приведение типа (C++) (см static cast)
|
3
|
++
|
Префиксный инкремент
|
Справа налево
|
--
|
Префиксный декремент
|
+
|
Унарный плюс
|
-
|
Унарный минус
|
!
|
Логическое НЕ
|
~
|
Побитовое НЕ
|
(type)
|
Приведение типа
|
*
|
Разыменование указателя
|
&
|
Взятие адреса объекта
|
sizeof
|
Sizeof (размер)
|
new , new[]
|
Выделение динамической памяти (C++)
|
delete , delete[]
|
Освобождение динамической памяти (C++)
|
4
|
.*
|
Указатель на член (C++)
|
Слева направо
|
->*
|
Указатель на член (C++)
|
5
|
*
|
Умножение
|
/
|
Деление
|
%
|
Получение остатка от деления
|
6
|
+
|
Сложение
|
-
|
Вычитание
|
7
|
<<
|
Побитовый сдвиг влево
|
>>
|
Побитовый сдвиг вправо
|
8
|
<
|
Меньше
|
<=
|
Меньше или равно
|
>
|
Больше
|
>=
|
Больше или равно
|
9
|
==
|
Сравнение
|
!=
|
Неравенство
|
10
|
&
|
Побитовое И (and)
|
11
|
^
|
Побитовое исключающее ИЛИ (xor)
|
12
|
|
|
Побитовое ИЛИ (or)
|
13
|
&&
|
Логическое И
|
14
|
||
|
Логическое ИЛИ
|
15
|
?:
|
Тернарная условная операция
|
Справа налево
|
=
|
Присваивание
|
+=
|
Сложение, совмещённое с присваиванием
|
-=
|
Вычитание, совмещённое с присваиванием
|
*=
|
Умножение, совмещённое с присваиванием
|
/=
|
Деление, совмещённое с присваиванием
|
%=
|
Вычисление остатка от деления, совмещённое с присваиванием
|
<<=
|
Побитовый сдвиг влево, совмещённый с присваиванием
|
>>=
|
Побитовый сдвиг вправо, совмещённый с присваиванием
|
&=
|
Побитовое «И», совмещённое с присваиванием
|
|=
|
Побитовое «ИЛИ», совмещённое с присваиванием
|
^=
|
Побитовое «исключающее ИЛИ» (xor), совмещённое с присваиванием
|
throw
|
Оператор создания исключения (C++)
|
16
|
,
|
Оператор «запятая»
|
Слева направо
|
Описание
Компилятор использует таблицу приоритетов для определения порядка вычисления операторов.
- Например,
++x*3 был бы двусмысленным без каких-либо правил приоритетов. По таблице можно сказать, что x сначала связывается с оператором ++, и только затем с оператором *, поэтому независимо от действия оператора ++, это действие только над x (а не над x*3 ). Таким образом, выражение эквивалентно (++x , x*3 ).
- Аналогично с кодом
3*x++ , где таблица утверждает, что инкремент применяется только к x а не к 3*x . Функционально это выражение эквивалентно (tmp=x, x++, tmp= 3*tmp, tmp ), если выразить временную переменную как tmp.
Связывание операторов в стандартах Си и C++ определено через грамматику языка, а не через таблицу. Это может создать конфликт. Например, в языке Си синтаксис условного оператора таков:
logical-OR-expression ? expression : conditional-expression
А в языке C++:
logical-OR-expression ? expression : assignment-expression
Из-за этого выражение:
e = a < d ? a++ : a = d
будет воспринято по-разному в этих двух языках. В Си выражение синтаксически некорректно, так как результат условного оператора не может служить lvalue (то есть, левой частью оператора присваивания).
В C++, выражение будет разобрано как корректное:[1]
e = (a < d ? a++ : (a = d))
Приоритеты побитовых логических операторов несколько неинтуитивны[2]. Концептуально & и | являются такими же арифметическими операторами как * и + соответственно.
Выражение a & b == 7 синтаксически воспринимается как a & (b == 7) , но выражение a + b == 7 эквивалентно (a + b) == 7 . Из-за этого часто требуется пользоваться скобками для явного задания порядка вычислений.
Синонимы операторов в C++
В стандарте C++ определены[3]
диграфы для некоторых операторов:
Диграф |
Эквивалентная строка
|
and |
&&
|
bitand |
&
|
and_eq |
&=
|
or |
||
|
bitor |
|
|
or_eq |
|=
|
xor |
^
|
xor_eq |
^=
|
not |
!
|
not_eq |
!=
|
compl |
~
|
Диграфы могут использоваться точно так же как и операторы, являются синонимами операторов. Например, диграф «bitand » может использоваться для замены операторов «побитовое И» и «получение адреса» или в определении ссылочных типов. Так, код «int bitand ref = n; » эквивалентен коду «int & ref = n; ».
Стандарт ANSI/ISO C определяет перечисленные диграфы в виде констант #define (см. препроцессор). Константы определены в заголовочном файле «iso646.h ». Для совместимости с Си стандарт C++ определяет фиктивный заголовочный файл «ciso646 ».
Примечания
- Does the C/C++ ternary operator actually have the same precedence as assignment operators? (неопр.) Stack Overflow. Дата обращения: 22 сентября 2019. Архивировано 6 августа 2020 года.
- Chistory (неопр.). Дата обращения: 11 января 2013. Архивировано из оригинала 22 июня 2013 года.
- ISO/IEC JTC1/SC22/WG21 - Комитет по стандартизации C++. ISO/IEC 14882:1998(E) Язык программирования C++ (англ.) (рус.). — Международная группа по стандартизации языка программирования C++, 1998. — С. 40—41.
Ссылки
|
|