Interested Article - Операторы в C и C++

Язык программирования 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 );
Операция модуль ( остаток от деления целых чисел) % 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 );
Декремент префиксный -- -- a Да Да R & T :: operator -- (); R & operator -- ( T a );
суффиксный (постфиксный) -- a -- Да Да R T :: operator -- ( int ); R operator -- ( T a , int );

Операторы сравнения

Операция (выражение) Оператор Синтаксис выражения Перегружаемый Реализован в Си Пример
Член типа 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 );
Побитовый сдвиг влево << 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 += 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 );
Вычисление остатка от деления, совмещённое с присваиванием %= 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 );
Побитовый сдвиг вправо, совмещённый с присваиванием >>= 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 -> ();
н/д
Обращение к члену структуры («член b объекта a ») a . b Нет Да н/д
Член, на который указывает b в объекте, на который указывает a 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 (a)
sizeof ( type )
Нет Да н/д
( выравнивание ) alignof ( type ) или _Alignof ( type ) Нет Да н/д
Интроспекция typeid (a)
typeid ( type )
Нет Нет н/д
Приведение типа ( type ) a Да Да T :: operator R (); н/д
Выделение памяти 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. Оператор « % » работает только с целыми числами. Для чисел с плавающей точкой используйте функцию fmod () из файла « math.h ».
  2. Чтобы отличить префиксный и суффиксный (постфиксный) операторы друг от друга, у постфиксных операторов добавлен неиспользуемый формальный параметр типа int . Часто этому параметру даже не дают имя.
  3. В библиотеке « iostream » операторы « << » и « >> » используются для работы с потоковым выводом и вводом.
  4. По стандарту C99, сдвиг вправо отрицательного числа — implementation defined behavior (см. неуточняемое поведение ). Многие компиляторы , в том числе gcc (см. от 22 сентября 2019 на Wayback Machine (англ.) ), реализуют арифметический сдвиг , но стандарт не запрещает реализовывать логический сдвиг .
  5. Тип возвращаемого значения оператора « operator -> () » должен быть типом, к которому применим оператор « -> », например, указателем. Если « x » имеет тип « C », и класс « C » перегружает оператор « operator -> () », выражение « x -> y » раскрывается как « x . operator -> () -> y ».
  6. См. пример в от 17 мая 2013 на Wayback Machine (англ.) «Реализация оператора ->* для умных указателей » Скотта Майерса из журнала « Dr. Dobb’s journal », выпуск за октябрь 1999 года.
  7. Оператор sizeof , обычно, записывают со скобками. Если операнд — имя переменной, указание скобок необязательно. Если операнд — имя типа, скобки обязательны.
  8. Стандарт языка C++ определяет оператор alignof . Аналогичный оператор в стандарте языка Си называется _Alignof .
  9. Для оператора приведения типа тип возвращаемого значения явно не указывается, так как совпадает с именем оператора.

Приоритеты операторов

В данной таблице указаны приоритеты операторов и их ассоциативность. Операторы, указанные в таблице выше (раньше), имеют более высокий приоритет (приоритет вычисления). При рассмотрении выражения, операторы, имеющие более высокий приоритет, будут вычислены раньше операторов с низким приоритетом. Если несколько операторов указаны в одной ячейке, то они имеют одинаковый приоритет и вычисляются в последовательности, задаваемой ассоциативностью. Приоритеты операторов не изменяются при их перегрузке.


Этой таблицы приоритетов в большинстве случаев бывает достаточно, за исключением следующих случаев. Тернарный оператор «?:» может содержать в среднем выражении оператор «запятая» или присваивание, но код « 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 (размер)
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++, выражение будет разобрано как корректное:

e = (a < d ? a++ : (a = d))

Приоритеты побитовых логических операторов несколько неинтуитивны . Концептуально & и | являются такими же арифметическими операторами как * и + соответственно.

Выражение a & b == 7 синтаксически воспринимается как a & ( b == 7 ) , но выражение a + b == 7 эквивалентно ( a + b ) == 7 . Из-за этого часто требуется пользоваться скобками для явного задания порядка вычислений.

Синонимы операторов в C++

В стандарте C++ определены диграфы для некоторых операторов:

Диграф Эквивалентная строка
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 ».

Примечания

  1. Stack Overflow. Дата обращения: 22 сентября 2019. 6 августа 2020 года.
  2. . Дата обращения: 11 января 2013. Архивировано из 22 июня 2013 года.
  3. . ISO/IEC 14882:1998(E) Язык программирования C++ (англ.) . — Международная группа по стандартизации языка программирования C++, 1998. — С. 40—41.

Ссылки

  • от 26 февраля 2019 на Wayback Machine (англ.) «Операторы C++» на сайте cppreference.com.
  • от 4 марта 2013 на Wayback Machine (англ.) «Префиксные и постфиксные операторы в языках Си и C++» на сайте msdn.microsoft.com.
  • ISO/IEC 14882
Источник —

Same as Операторы в C и C++