Перегрузка операторов. Общие сведения. Перегрузка унарных операторов –, !, ++, ––
Содержание
- 1. Понятие перегрузки операторов
- 2. Требования и ограничения, которые накладываются при перегрузке операторов
- 3. Перечень операторов, допускающих и не допускающих перегрузку
- 4. Общая форма перегрузки оператора в классе. Ключевое слово operator
- 5. Особенности перегрузки унарных операторов
- 6. Пример перегрузки унарного оператора – (минус)
- 7. Пример перегрузки унарного оператора ! (отрицание)
- 8. Пример перегрузки унарных операторов ++, — (инкремент, декремент)
- 9. Пример перегрузки унарного оператора ~ (дополнение к 1)
- Связанные темы
Поиск на других ресурсах:
1. Понятие перегрузки операторов
В языке C# существует возможность задавать оператор (+, –, != и другие) для конкретного класса или структуры, который может быть использован для выполнения каких-либо действий. Как правило, эти действия производятся над объектами этого класса (структуры). Представление действий посредством понятных (естественных) операторов улучшает восприятие программного кода. Таким образом, использование известных операторов может быть как для базовых типов (int, double, char и других), так и для пользовательских классов (пользовательских типов).
Механизм задания стандартных операторов для заданного класса или структуры с целью улучшения читаемости программ называется перегрузкой операторов. Операторы можно перегружать как для классов, так и для структур.
Многие стандартные классы реализуют перегрузку многих операторов. Например, класс String имеет перегруженные операторы == и !=, что позволяет удобно сравнивать две строки на равенство (неравенство).
⇑
2. Требования и ограничения, которые накладываются при перегрузке операторов
При выполнении перегрузки операторов в классах необходимо соблюдать ряд требований:
- для реализации перегрузки нужно использовать общедоступные статические методы, объявляемые в классе;
- в операторном методе запрещается использовать модификаторы ref или out;
- в операторном методе тип параметра или тип возвращаемого значения должен совпадать с типом (классом), в котором этот оператор перегружается.
Также на перегрузку операторов накладываются следующие ограничения:
- перегрузка не изменяет приоритет операторов;
- при перегрузке оператора количество используемых операндов изменить невозможно;
- не все операторы можно перегружать.
⇑
3. Перечень операторов, допускающих и не допускающих перегрузку
Не все операторы допускают перегрузку. В таблице ниже представлены операторы, которые можно перегружать.
Операторы | Категория операторов |
– | Изменение знака на противоположный |
! | Логическое отрицание |
~ | Дополнение к 1 |
++, — | Инкремент, декремент |
true, false | Истинность объекта |
+, –, *, /, % | Арифметические операторы |
&&, || | Логические операторы |
&, |, ^, <<, >> | Битовые операторы |
==, !=, <, >, <=, >= | Операторы сравнения |
[] | Операция доступа к элементам массива |
() | Операции преобразования |
Ниже представлен перечень операторов, не допускающих перегрузки.
Операторы | Категория операторов |
+=, -=, *=, /=, %=, &=, |=, ^=, <<=, >>= | Сокращенные операторы присваивания |
= | Присваивание |
. | Доступ к элементам |
?: | Тернаный условный оператор |
new | Создание объекта |
as, is, typeof | Получение информации о типе |
->, sizeof, *, & | Доступны в небезопасном коде |
⇑
4. Общая форма перегрузки оператора в классе. Ключевое слово operator
Для перегрузки оператора в классе используется ключевое слово operator по следующему синтаксису.
public static return_type operator op(parameters)
здесь
- op – перегружаемый оператор (+, –, != и т.д.);
- return_type – тип, который возвращается при использовании оператора op;
- parameters – список параметров, которые получает оператор. Для унарных операторов (-, !, ++, —) parameters содержит только 1 параметр, для бинарных – 2 параметра.
⇑
5. Особенности перегрузки унарных операторов
К унарным операторам, которые можно перегружать, относятся операторы:
- — – унарный минус;
- ! – логическое отрицание;
- ++ – инкремент;
- –– – декремент.
Поскольку все перегруженные операторы являются статическими методами, то они не получают указатель this текущего экземпляра. Как следствие, все операторные методы, перегружающие унарные операторы, получают один параметр. Этот параметр должен быть типа класса.
В наиболее общем случае перегрузка унарного оператора в классе ClassName выглядит следующим образом
class ClassName { ... public static return_type op(ClassName param) { // Выполняемые действия // ... } }
здесь
- op – обозначение одного из унарных операторов -, !, ++, —;
- param – некоторый параметр;
- return_type – возвращаемый тип. Это может быть любой тип, используемый в программе.
После этого оператор op можно использовать для класса ClassName примерно следующим образом
value = op Obj;
здесь
- value – получаемое значение типа return_type;
- Obj – экземпляр класса ClassName.
Например, если перегружен оператор ++, то вызов будет
value = ++Obj;
⇑
6. Пример перегрузки унарного оператора – (минус)
В примере приводится перегрузка унарного оператора – (минус) на примере класса, описывающего комплексное число. С помощью перегруженного оператора знаки вещественной и мнимой части комплексного числа изменяются на противоположные.
using System; namespace ConsoleApp6 { class Complex { // Внутренние поля класса private double re, im; // Конструктор public Complex(double re, double im) { this.re = re; this.im = im; } // Свойства доступа к внутренним полям public double Re { get { return re; } set { re = value; } } public double Im { get { return im; } set { im = value; } } // Операторный метод, который перегружает унарный оператор '-' public static Complex operator-(Complex cm) { return new Complex(-cm.re, -cm.im); } // Метод, выводящий состояние текущего экземпляра public void Print(string msg) { Console.Write(msg + " => "); Console.Write(re); if (im >= 0) Console.Write("+"); Console.WriteLine(im.ToString() + "*j"); } } internal class Program { static void Main(string[] args) { // Создать экземпляр класса Complex Complex c1 = new Complex(2.4, -3.8); // Вывести состояние экземпляра c1.Print("c1"); // c1 => 2.4-3.8*j // Создать ссылку на Complex Complex c2; // Присвоить ссылке результат возврата // из перегруженного оператора - c2 = -c1; // вызывается метод Complex::operator-(c1); // Вывести состояние экземпляра с2 c2.Print("c2"); // c2 => -2.4+3.8*j Console.ReadKey(); } } }
Результат
c1 => 2.4-3.8*j c2 => -2.4+3.8*j
⇑
7. Пример перегрузки унарного оператора ! (отрицание)
На примере класса, описывающего треугольник по его сторонам, реализована перегрузка унарного оператора !. Операторный метод возвращает значение true, если треугольник равносторонний с заданной точностью. В противном случае возвращается false.
В примере демонстрируется только минимальный набор методов с целью демонстрации перегрузки оператора ! (отрицание).
using System; namespace ConsoleApp6 { class Triangle { // Внутренние поля класса - координаты крайних точек треугольника private double x1, y1, x2, y2, x3, y3; // Конструктор public Triangle(double x1, double y1, double x2, double y2, double x3, double y3) { this.x1 = x1; this.y1 = y1; this.x2 = x2; this.y2 = y2; this.x3 = x3; this.y3 = y3; } // Операторный метод, перегружающий унарный оператор ! // Метод возвращает true, если длины сторон равны // с точностью 5 знаков после запятой. public static bool operator!(Triangle tr) { // Точность - 5 знаков после запятой double eps = 1E-5; // Расстояние между точками 1 - 2 double a = Math.Sqrt((tr.x1 - tr.x2) * (tr.x1 - tr.x2) + (tr.y1 - tr.y2) * (tr.y1 - tr.y2)); // Расстояние между точками 1 - 3 double b = Math.Sqrt((tr.x1 - tr.x3) * (tr.x1 - tr.x3) + (tr.y1 - tr.y3) * (tr.y1 - tr.y3)); // Расстояние между точками 2 - 3 double c = Math.Sqrt((tr.x2 - tr.x3) * (tr.x2 - tr.x3) + (tr.y2 - tr.y3) * (tr.y2 - tr.y3)); return (Math.Abs(a - b) < eps) && (Math.Abs(b - c) < eps) && (Math.Abs(a - c) < eps); } } internal class Program { static void Main(string[] args) { // Создать экземпляр класса Triangle Triangle tr = new Triangle(1, 1, 2, 2, 3, 1); // Вызвать перегруженный оператор Triangle::operator!() if (!tr) Console.WriteLine("The sides of the triangle are equal"); else Console.WriteLine("The sides of the triangle are not equal"); Console.ReadKey(); } } }
Результат
The sides of the triangle are not equal
⇑
8. Пример перегрузки унарных операторов ++, — (инкремент, декремент)
Унарные операторы инкремента (++) и декремента (—) также могут быть перегружены. В C# нет отличия при перегрузке префиксной или постфиксной формы операторов инкремента и декремента. Перегружается префиксная форма, которая имеет такое же действие как и постфиксная форма.
В примере реализован класс Point, описывающий точку на координатной плоскости. С целью демонстрации в классе перегружаются операторные функции
public static Point operator++(Point pt); public static Point operator--(Point pt);
В этих функциях значение координат x, y соответственно увеличивается и уменьшается на 1.
using System; namespace ConsoleApp6 { class Point { // Внутренние поля - координаты точки private double x, y; // Конструктор public Point(double x, double y) { this.x = x; this.y = y; } // Свойства доступа к внутренним полям public double X { get { return x; } set { x = value; } } public double Y { get { return y; } set { y = value; } } // Метод, который выводит внутреннее состояние класса public void Print(string msg) { Console.WriteLine(msg + " => ( " + x + "; " + y + ")"); } // Методы, перегружающие операторы инкремента (++) и декремента (--) public static Point operator++(Point pt) { return new Point(pt.x + 1, pt.y + 1); } public static Point operator--(Point pt) { return new Point(pt.x - 1, pt.y - 1); } } internal class Program { static void Main(string[] args) { // 1. Объявить экземпляр класса Point Point pt1 = new Point(2, 8); // 2. Вызвать постфиксную форму оператора инкремента ++ pt1++; // вызывается перегруженный оператор ++ для класса Point // 3. Вывести состояние экземпляра pt1 pt1.Print("pt1"); // pt1 => ( 3; 9) // 4. Вызвать префиксную форму оператора декремента -- --pt1; // 5. Вывести состояние экземпляра pt1 повторно pt1.Print("pt1"); // pt1 => ( 2; 8) Console.ReadKey(); } } }
Результат
pt1 => ( 3; 9) pt1 => ( 2; 8)
⇑
9. Пример перегрузки унарного оператора ~ (дополнение к 1)
В примере демонстрируется перегрузка унарного оператора ~ (тильда).
Объявляется класс Integer, описывающий целое неотрицательное число. В классе объявляется операторный метод
public static Integer operator~(Integer num)
который перегружает оператор ~. Метод возвращает экземпляр класса Integer, в котором число реверсировано (читается в обратном порядке).
using System; namespace ConsoleApp6 { // Класс, описывающий целочисленное значение, // которое может быть только положительным и имеет дополнительные свойства class Integer { // Внутреннее поле - число private int value; // Конструктор public Integer(int value) { if (value < 0) this.value = -value; else this.value = value; } // Свойства доступа public int Value { get { return value; } set { if (value < 0) this.value = -value; else this.value = value; } } // Метод перегрузки оператора ~ // Метод инвертирует число. Например, 2351 => 1532 public static Integer operator~(Integer num) { int result = 0; int t; int pow; // Определить порядок числа t = num.Value / 10; pow = 1; while (t > 0) { t = t / 10; pow *= 10; } // Сформировать результирующее число t = num.Value; while (t>0) { result = result + (t % 10) * pow; t = t / 10; pow = pow / 10; } return new Integer(result); } // Метод, выводящий внутреннее поле public void Print(string msg) { Console.WriteLine(msg + " => " + value); } } internal class Program { static void Main(string[] args) { // Объявить экземпляр класса Integer Integer num1 = new Integer(25631); // Вывести значение экземпляра num1.Print("num1"); // Вызвать метод, который перегружает оператор ~ Integer num2 = ~num1; // Вывести результат num2.Print("num2"); Console.ReadKey(); } } }
Результат
num1 => 25631 num2 => 13652
⇑
Связанные темы
- Перегрузка бинарных операторов. Перегрузка операторов +, –, *, /, %. Вложение произвольной логики при перегрузке
- Перегрузка операторов сравнения ==, !=, <, >, <=, >=. Перегрузка логических побитовых операторов
⇑