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

Материал из Википедии — свободной энциклопедии
Перейти к: навигация, поиск

Языки программирования Си и Си++ поддерживают сходный набор операторов. Некоторые из операторов недоступны в языке Си. Кроме того Си не поддерживает перегрузку операторов.

Операторы &&, || и , (оператор comma), при условии что не перегружены, определяют sequence point (точку следования) после вычисления первого операнда.

Также в Си++ имеются операторы приведения типа: const_cast, static_cast, dynamic_cast и reinterpret_cast, которые не были включены в таблицу для краткости.

Многие операторы языков Си и Си++ также доступны в других языках, например C#, Java, Perl, PHP. При этом обычно сохраняется их приоритет, ассоциативность и семантика.

Таблицы[править | править вики-текст]

Символами a, b и c обозначаются значения (литералы, значения переменных, возвращаемые значения), имена объектов, lvalue.

«Перегружаемый» обозначает возможность перегрузки оператора в языке C++. «Реализован в Си» означает, существует ли такой оператор в языке Си.

В колонке «пример» показано, как объявлять перегруженный оператор.

Арифметические операторы[править | править вики-текст]

Оператор Синтаксис Перегружаемый Реализован в Си Пример (R, S и T обозначают тип)
Член типа 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);
Прим.: C++ использует неименованный параметр int для различения префиксной и суффиксной форм.
Декремент префиксный --a Да Да R T::operator --(); R operator --(T a);
суффиксный a-- Да Да R T::operator --(int); R operator --(T a, int);
Прим.: C++ использует неименованный параметр int для различения префиксной и суффиксной форм.

Операторы сравнения[править | править вики-текст]

   Оператор       Синтаксис  Перегружаемый Реализован в Си Пример (R, S и T обозначают тип)
Член типа 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);

Логические операторы[править | править вики-текст]

Оператор   Синтаксис   Перегружаемый Реализован в Си Пример (R, S и T обозначают тип)
Член типа 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);

Побитовые операторы[править | править вики-текст]

Оператор   Синтаксис   Перегружаемый Реализован
в Си
Пример (R, S и T обозначают тип)
Член типа 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);
Побитовое исключающее ИЛИ (XOR) a ^ b Да Да R T::operator ^(S b); R operator ^(T a, S b);
Побитовый левый сдвиг[note 2] a << b Да Да R T::operator <<(S b); R operator <<(T a, S b);
Побитовый правый сдвиг[note 2][note 3] a >> b Да Да R T::operator >>(S b); R operator >>(T a, S b);

Составное присваивание[править | править вики-текст]

Оператор   Синтаксис       Значение     Перегружаемый Реализован в Си Пример (R, S и T обозначают тип)
Член типа 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);
Присваивание с побитовым И 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);
Присваивание с побитовым исключающим ИЛИ (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 3] a >>= b a = a >> b Да Да R T::operator >>=(S b); R operator >>=(T a, S b);

Операторы работы с указателями и членами класса[править | править вики-текст]

Оператор Синтаксис Перегружаемый Реализован в Си Пример (R, S и T обозначают тип)
Член типа 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 4]
Н/Д
Обращение к члену структуры («член b объекта a») a.b Нет Да Н/Д
Член, на который указывает b, в объекте на который указывает a[note 5] a->*b Да Нет R T::operator ->*(S b); R operator ->*(T a, S b);
Член, на который указывает b, в объекте a a.*b Нет Нет Н/Д

Другие операторы[править | править вики-текст]

Оператор Синтаксис Перегружаемый Реализован в Си Пример (R, S и T обозначают тип)
Член типа T Определение вне класса
Вызов функции
См. Function object.
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 Нет Да Н/Д
Size-of (размер) sizeof(a)[note 6]
sizeof(type)
Нет Да Н/Д
Align-of (выравнивание) alignof(type)
or _Alignof(type)[note 7]
Нет Да Н/Д
Идентификация типа 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).
  2. 1 2 В библиотеке iostream операторы << и >> используются для работы с потоковым выводом и вводом.
  3. 1 2 По стандарту C99, правый сдвиг на отрицательное число — implementation defined. Многие реализации, в том числе GCC используют арифметический сдвиг, а некоторые могут реализовывать логический сдвиг.
  4. Возвращаемый тип operator->() должен быть типом, к которому применим оператор ->, например, указателем. Если x имеет тип C, и класс C перегружает оператор operator->(), выражение x->y раскрывается как x.operator->()->y.
  5. Пример имеется в «Implementing operator->* for Smart Pointers», Scott Meyers // DrDobb’s, Oct 1999.
  6. Скобки являются необязательными, если берется размер переменной, и обязательны при работе с типами. Тем не менее, обычно оператор записывают со скобками.
  7. C++ определяет оператор alignof, тогда как в языке C аналогичный оператор называется _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 Size-of (размер)
new, new[] Выделение динамической памяти (C++)
delete, delete[] Освобождение динамической памяти (C++)
4 .* Указатель на член (C++) Слева направо
->* Указатель на член (C++)
5 * Умножение
/ Деление
% Взятие модуля (остаток от деления)
6 + Сложение
- Вычитание
7 << Побитовый сдвиг влево
>> Побитовый сдвиг вправо
8 < Менее
<= Менее или равно
> Более
>= Более или равно
9 == Равенство
!= Неравенство
10 & Побитовое И
11 ^ Побитовое исключающее или (XOR)
12 | Побитовое ИЛИ
13 && Логическое И
14 || Логическое ИЛИ
15 ?: Условный оператор Справа налево
16 = Прямое присваивание
+= Присваивание со сложением
-= Присваивание с вычитанием
*= Присваивание с умножением
/= Присваивание с делением
%= Присваивание с взятием остатка от деления
<<= Присваивание с побитовым сдвигом влево
>>= Присваивание с побитовым сдвигом вправо
&= Присваивание с побитовым И
^= Присваивание с побитовым XOR
|= Присваивание с побитовым OR
17 throw Оператор создания исключения (C++)
18 , Оператор Comma Слева направо

Примечания[править | править вики-текст]

Таблица приоритетов определяет приоритет в цепочках выражений, когда порядок вычислений не был явно задан скобками.

  • Например, ++x*3 был бы двусмысленным без каких-либо правил приоритетов. По таблице можно сказать, что x сначала связывается с оператором ++, и только затем с оператором *, поэтому независимо от действия оператора ++, это действие только над x (а не над x*3). Таким образом, выражение эквивалентно (++x, x*3).
  • Аналогично с кодом 3*x++, где таблица утверждает, что инкремент применяется только к x а не к 3*x. Функционально это выражение эквивалентно (tmp=x, x++, tmp=3*tmp, tmp), если выразить временную переменную как tmp.
Приоритеты и связывание

Связывание операторов в стандартах C и C++ определено через грамматику языка, а не через таблицу. Это может создать конфликт. Например, в языке Си синтаксис условного оператора таков:

logical-OR-expression ? expression : conditional-expression

А в языке Си++:

logical-OR-expression ? expression : assignment-expression

Из-за этого выражение:

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

будет воспринято по-разному в этих двух языках. В Си выражение синтаксически некорректно, но многие компиляторы воспринимают его как:

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

Этот вариант ошибочен, так как результат условного оператора не является lvalue.

В C++, выражение будет разобрано как корректное:

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

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

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

Синонимы операторов в C++[править | править вики-текст]

В C++ определены[2] ключевые слова, являющиеся синонимами к некоторым операторам: and (&&), bitand (&), and_eq (&=), or (||), bitor (|), or_eq (|=), xor (^), xor_eq (^=), not (!), not_eq (!=), compl (~). Они могут использоваться точно так же как и оператор. Фактически они являются другим вариантом символьной записи тех же операторов. Например, bitand может использоваться не только для замены побитового оператора И, но и как оператор взятия адреса, или даже при задании ссылочных типов (допустима запись int bitand ref = n; что эквивалентно int & ref = n;).

В стандарте ANSI C (ISO C) эти ключевые слова определены как макросы препроцессора в заголовочном файле <iso646.h>. Для совместимости, в C++ существует фиктивный заголовочный файл <ciso646>.

Примечания[править | править вики-текст]

  1. Chistory
  2. ISO/IEC JTC1/SC22/WG21 - The C++ Standards Committee ISO/IEC 14882:1998(E) Programming Language C++. — International standardization working group for the programming language C++. — P. 40–41.

Ссылки[править | править вики-текст]