Оператор не имеет результата требуется оператор с побочным действием

Оператор не имеет результата требуется оператор с побочным действием thumbnail

Текущая версия страницы пока не проверялась опытными участниками и может значительно отличаться от версии, проверенной 14 августа 2019;
проверки требуют 12 правок.

Язык программирования C++, поддерживает все операторы своего прародителя Си и дополнен новыми операторами и возможностями.

  • например операторами приведения типа:
    • const_cast
    • static_cast
    • dynamic_cast
    • reinterpret_cast
  • возможность перегрузки операторов;

После вычисления первого операнда для неперегруженных операторов «&&», «||» и «,» (оператор «запятая», англ. comma) компилятор вставляет точку следования (англ. sequence point), гарантирующую, что все побочные эффекты (например, оператор «постфиксный ++») будут выполнены до начала вычисления второго операнда.

Языки с Си-подобным синтаксисом (например, Java, C#, PHP и другие) часто заимствуют операторы Cи/C++ с сохранением не только поведения, но также приоритета и ассоциативности.

Таблицы[править | править код]

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 += ba = a + bДаДаR T::operator +=(S b);R operator +=(T a, S b);
Вычитание, совмещённое с присваиванием-=a -= ba = a bДаДаR T::operator -=(S b);R operator -=(T a, S b);
Умножение, совмещённое с присваиванием*=a *= ba = a * bДаДаR T::operator *=(S b);R operator *=(T a, S b);
Деление, совмещённое с присваиванием/=a /= ba = a / bДаДаR T::operator /=(S b);R operator /=(T a, S b);
Вычисление остатка от деления, совмещённое с присваиванием[note 1]%=a %= ba = a % bДаДаR T::operator %=(S b);R operator %=(T a, S b);
Побитовое «И» (AND), совмещённое с присваиванием&=a &= ba = a & bДаДаR T::operator &=(S b);R operator &=(T a, S b);
Побитовое «ИЛИ» (or), совмещённое с присваиванием|=a |= ba = a | bДаДаR T::operator |=(S b);R operator |=(T a, S b);
Побитовое «исключающее ИЛИ» (xor), совмещённое с присваиванием^=a ^= ba = a ^ bДаДаR T::operator ^=(S b);R operator ^=(T a, S b);
Побитовый сдвиг влево, совмещённый с присваиванием<<=a <<= ba = a << bДаДаR T::operator <<=(S b);R operator <<=(T a, S b);
Побитовый сдвиг вправо, совмещённый с присваиванием[note 4]>>=a >>= ba = 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 в объекте aa.*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НетНетн/д
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. 1 2 Оператор «%» работает только с целыми числами. Для чисел с плавающей точкой используйте функцию fmod() из файла «math.h».
  2. 1 2 Чтобы отличить префиксный и суффиксный (постфиксный) операторы друг от друга, у постфиксных операторов добавлен неиспользуемый формальный параметр типа int. Часто этому параметру даже не дают имя.
  3. 1 2 В библиотеке «iostream» операторы «<<» и «>>» используются для работы с потоковым выводом и вводом.
  4. 1 2 По стандарту C99, сдвиг вправо отрицательного числа — implementation defined behavior (см. неуточняемое поведение). Многие компиляторы, в том числе gcc (см. документацию (англ.)), реализуют арифметический сдвиг, но стандарт не запрещает реализовывать логический сдвиг.
  5. ↑ Тип возвращаемого значения оператора «operator->()» должен быть типом, к которому применим оператор «->», например, указателем. Если «x» имеет тип «C», и класс «C» перегружает оператор «operator->()», выражение «x->y» раскрывается как «x.operator->()->y».
  6. ↑ См. пример в статье (англ.) «Реализация оператора ->* для умных указателей» Скотта Майерса из журнала «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)Приведение типа
*Разыменование указателя
&Взятие адреса объекта
sizeofSizeof (размер)
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

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

будет воспринято по-разному в этих двух языках. В Си выражение синтаксически некорректно, так как результат условного оператора не может служить 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».

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

Ссылки[править | править код]

  • Статья (англ.) «Операторы C++» на сайте cppreference.com.
  • Статья (англ.) «Префиксные и постфиксные операторы в языках Си и C++» на сайте msdn.microsoft.com.
  • ISO/IEC 14882

Источник

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

Подпишись на группу Вконтакте и Телеграм-канал. Там еще больше полезного контента для программистов.
А на YouTube-канале ты найдешь обучающие видео по программированию. Подписывайся!

Перегрузка операторов в языке программирования C#

Перегрузка операций в С# похожа на переопределение методов. В языке С# в зависимости от типа оператора объявляется оно двумя способами.

// перегрузка унарного оператора
public static тип operator primer(тип_параметра операнд)
{
// запросы
}

// перегрузка бинарного оператора
public static тип operator primer (тип_параметра1 операнд1, тип_параметра1 операнд2)
{
// запросы
}

Метод объявляется модификаторами public и static. Оператор, который перегружается должен быть одного типа с операндами.

Во время перегрузки оператора его функция и свойство не теряется, он только выполняет еще одну операцию. К примеру, оператор + используется для работы со списком при этом его назначение к сложению целых чисел не меняется. Когда оператор + используется со строковыми данными, выполняется перегрузка оператора со стороны класса String.

В С# существует операторы «+», «-», «!», «==», «!=» и другие, которые предназначены для работы с типами данных.

Так как в языке программирования С# используются готовые алгоритмы базовых операций, в некоторых моментах важна перегрузка операторов. Например, когда используем оператор + для целых чисел он возвращает сумму чисел, а если использовать его для строковых типов данных тогда выполняется объединение двух строк.

string a = «Сергей «;
string b = «Сергеев»;
string c = a + b; //результат «Сергей Сергеев»

Перегрузка операторовпозволяет добавлять удобный синтаксис для работы с классом в среду программирования. Это возможность одна из сильных сторон объектно-ориентированного языка программирования С#.

Когда в классе присутствует перегруженный оператор, компилятор сначала выполняет поведение определенное пользователем, если не определено, то осуществляется стандартная реализация.

Перегрузка операторов С# делает код красивее. Мы задаем, редактируем, или сами можем придумывать поведение определенного нами класса. Используя унарные, бинарные операторы, операторы сравнения и логические операторы мы задаем определенное действие компилятору.

Например, есть класс «точки» с координатами х и у. Два целых числа, объединенные одним классом. Мы можем сложить эти две точки. Даже если компилятор умный, система не в состоянии понять какое сложение точек выполнить. Поэтому нужно перегружать оператор, чтобы объяснить компилятору, как системе себя вести и какое действие выполнить. Возможно системе нужно сложить между собой все х или же сложить все y. Компилятор может выполнить банальное сложение двух заданных точек, но, чтобы их выполнить, нужно указать поведение: например, когда нужно решать по формуле, для этого и служит перегрузка операторов. При ней пользователь С# задает правильное поведение, которое ожидается от класса.

Читайте также:  Побочные действия метформина со стороны органов

class MyPoint
{
int х, у,z// три координаты
public MyPoint() { х = у = z = 0; }

// Перегрузить бинарный оператор +.
public static MyPoint operator +(MyPoint op1, MyPoint op2)
{
MyPoint result = new MyPoint();
/* Сложить координаты двух точек и возвратить результат. */
result.х = op1.x + ор2.х; // Эти операторы выполняют
result.у = op1.y + ор2.у; // целочисленное сложение,
result.z = op1.z + op2.z; // сохраняя свое исходное назначение.
return result;
}

// Перегрузить бинарный оператор -.
public static MyPoint1 operator -(MyPoint1 op1, MyPoint1 op2)
{
MyPoint1 result = new MyPoint1();
/* Обратите внимание на порядок следования операндов:
op1 — левый операнд, а ор2 — правый операнд. */
result.х = op1.x — ор2.х; // Эти операторы
result.у = op1.y — ор2.у; // выполняют целочисленное
result.z = op1.z — op2.z; // вычитание
return result;
}

// Вывести координаты X, Y, Z.
public void Show()
{
Console.WriteLine(x + «, » + у + «, » + z);
}
}

Когда оператор перегружается, выполняет заданные поведение. Но его первоначальное назначение не меняется.

Перегружаемые операторы

Эти операторы можно перегрузить:

  1. Унарные операции +,  -,  !,  ++, — true, false;
  2. Бинарные операции +,  -,  *,  /,  %,  &,  |,  ,<<,  >>;
  3. Операции сравнения должны быть перегружены парами ==,  !=,<,  >,<=, >=.

Данный код перегружает операцию сравнения. Выбирая значение из операндов, компилятор выполняет сравнение и возвращает истину или ложь.

// Перегрузить оператор >.
public static bool operator >(dot op1, dot op2)
{
if(op1.val > op2.val) return true;
else return false;
}

// Перегрузить оператор <.
public static bool operator <( dot op1, dot op2)
{
if(op1.val < op2.val) return true;
else return false;
}

Еще один пример для перегрузки операторов ==, -, *= приведен ниже. В качестве класса выберем «квадратное уравнение» с коэффициентами.

class KvadratUravn
{
     public KvadratUravn ():this(0, 0, 0)
     {
     }
     public KvadratUravn (double a_, double b_, double c_)
     {
         a=a_;
         b=b_;
         c=c_;
     }
     public double A { get { return a; } set { a = value; } }
     public double B { get { return b; } set { b = value; } }
     public double C { get { return c; } set { c = value; } }
 
 
     public static bool operator ==( KvadratUravn raz, KvadratUravn dva)
     {
         return raz.a == dva.a &&
             raz.b == dva.b &&
             raz.c == dva.c;
     }
 
     public static bool operator !=( KvadratUravn raz, KvadratUravn dva)
     {
         return raz.a != dva.a &&
             raz.b != dva.b &&
             raz.c != dva.c;
     }
 
     public static KvadratUravn operator *( KvadratUravn raz, double dva)
     {
         return new KvadratUravn (raz.a * dva,
             raz.b * dva,
             raz.c * dva);
     }
 
     public static KvadratUravn operator -( KvadratUravn raz, double dva)
     {
         return new KvadratUravn (raz.a — dva,
             raz.b — dva,
             raz.c — dva);
     }
     public override string ToString()
     {
         return string.Format(«{0}, {1}, {2}», a, b, c);
     }
     private double a;
     private double b;
     private double c;
};
 
class Program
{
     static void Main(string[] args)
     {
KvadratUravn expr = new KvadratUravn (1, 2, 3);
            Console.WriteLine(expr.ToString());
         expr *= 5;
            Console.WriteLine(expr.ToString());
         expr -= 10;
            Console.WriteLine(expr.ToString());
     }
}

В следующем фрагменте кода программы приведен пример перегрузки логических операторов !, & и | для объектов класса Dot. Если один из точек координаты не равно нулю тогда программе покажет истину, а если все координаты равны нулю, тогда программа выдает значение ложь.

// Класс для хранения координат.
class dot
{
int х, у, z; // трехмерные координаты
public dot () { х = у = z = 0; }
public static bool operator |(dot op1, dot op2)
{
if( ((op1.x != 0) || (op1.у != 0) || (op1.z != 0)) | ((op2.x != 0) || (op2.у != 0) || (op2.z != 0)) )
return true;
else
return false;
}

// Перегрузить логический оператор &.
public static bool operator &(dot op1, dot op2)
{
if( ((op1.x != 0) && (op1.у != 0) && (op1.z != 0)) & ((op2.x != 0) && (op2.y != 0) && (op2.z != 0)) )
return true;
else
return false;
}

// Перегрузить логический оператор !.
public static bool operator !(dot op)
{
if((op.x != 0) || (op.y != 0) || (op.z != 0))
return false;
else return true;
}

// Вывести координаты X, Y, Z.
public void Show()
{
Console.WriteLine(x + «, » + у + «, » + z);
}
}

Перегрузка операторов C# заключение

Важно помнить, что на перегрузку операторов есть ограничения. Нельзя использовать оператор присваивания =. Кроме них есть операторы, которые в языке С# еще не определены. Так же, нельзя изменить количество операндов. Для бинарных операторов нельзя задавать три аргумента. Поскольку в его синтаксисе указан только два аргумента.  

Чтобы создать легкий интерфейс, важно использовать перегрузки операторов. К тому же, учитывая их, вы упрощаете набранный код программы. Данное действие над операторами интересно тем, что пользователь сможет внедрить в программу свои правила и требования, контролируя функции операторов.

Также рекомендую прочитать статью Override C# | Переопределение методов C#. А также подписывайтесь на группу ВКонтакте, Telegram и YouTube-канал. Там еще больше полезного и интересного для программистов.

Источник