Перегрузка операторов
Перегрузка операторов является одной из разновидностью Полиморфизма.
В С++ есть возможность распространения действия стандартных операций на операнды абстрактных типов данных. Для того, чтобы переопределить одну из стандартных операций для работы с операндами абстрактных типов, программист должен написать функцию с именем operator знак, где знак - обозначение этой операции (например, + - | += и т.д.).
Однако в языке существует несколько ограничений, накладываемых на переопределение операторов:
- Нельзя создавать новые символы операций.
- Нельзя переопределять операции:
:: * (разыменование, а не бинарное умножение) ?: sizeof ## # .
- Cимвол унарной операции не может использоваться для переопределения бинарной операции и наоборот. Например, символ « можно использовать только для бинарной операции, ! - только для унарной, а & - и для унарной, и для бинарной.
- Переопределение операций не меняет ни их приоритетов, ни порядка их выполнения (слева направо или справа налево).
- При перегрузке операции компьютер не делает никаких предположений о ее свойствах. Это означает, что если стандартная операция += может быть выражена через операции + и =, т.е. а + = b эквивалентно а = а + b, то для переопределения операций в общем таких соотношений не существует, хотя, конечно, программист может их обеспечить.
- Никакая операция не может быть переопределена для операндов стандартных типов.
- Как для унарной, так и для бинарной операции число аргументов функции operator () должно точно соответствовать числу операндов этой операции. Причем в перегрузку бинарного оператора принято передавать один аргумент, так как второй - неявный. Его имеет любая функция - член класса, это тот самый Указатель this языка CPP - указатель на объект, для которого вызван метод. Таким образом, в переопределение унарного оператора не следует передавать ничего вовсе.
Пример 1. Перегрузка бинарные операторы +-*%
#include <iostream>
using namespace std;
class Digit{
private:
int dig; // число
public:
Digit(){
dig=0;
}
Digit(int iDig){
dig=iDig;
}
void Show(){
cout<<dig<<"\n";
}
// перегружаем четыре оператора
// обратите внимания, все операторы
// бинарные, поэтому мы передаем в
// них один параметр - это операнд,
// который будет находиться справа
// от оператора в выражении
// левый операнд передается с помощью this
Digit operator+(const Digit &N)
{
Digit temp;
temp.dig=dig+N.dig;
return temp;
}
Digit operator-(const Digit &N)
{
Digit temp;
temp.dig=dig-N.dig;
return temp;
}
Digit operator*(const Digit &N)
{
Digit temp;
temp.dig=dig*N.dig;
return temp;
}
Digit Digit::operator%(const Digit &N)
{
Digit temp;
temp.dig=dig%N.dig;
return temp;
}
};
void main()
{
// проверяем работу операторов
Digit A(8),B(3);
Digit C;
cout<<"\Digit A:\n";
A.Show();
cout<<"\Digit B:\n";
B.Show();
cout<<"\noperator+:\n";
C=A+B;
C.Show();
cout<<"\noperator-:\n";
C=A-B;
C.Show();
cout<<"\noperator*:\n";
C=A*B;
C.Show();
cout<<"\noperator%:\n";
C=A%B;
C.Show();
}
Пример 2. Класс динамического массива.
Задача: Создайте класс динамического массива, в котором реализована проверка выхода за границы массива. Перегрузите операторы: [ ], =, +, -,++ (добавление элемента в конец массива), – (удаление элемента из конца массива).
/*
* File: classdynamicarray.cpp
* Author: darkfire
*
* Created on December 11, 2010, 7:32 PM
*/
#include <stdlib.h>
#include <iostream>
/* Создайте класс динамического массива, в котором реализована проверка выхода
* за границы массива.
* Перегрузите операторы: [ ], =, +, -,
* ++ (добавление элемента в конец массива),
* -- (удаление элемента из конца массива).
*/
using namespace std;
class A{
int size;
int *a;
public:
A(int size);//Конструктор. Создание, инициализация массива.
A(const A &aA);//Конструктор копирования
~A(){//деструктор
delete [] a;
}
int Get () const {// метод выводит размер динамического массива
return size;
}
int &operator[](int j){ //Перегруженный оператор []
return a [j];
}
A operator--(); //Перегруженный оператор -- префиксной формы. Размер массива уменьшается на единицу
A operator++(); //Перегруженный оператор ++
A operator+(const A &);
A operator=(const A &);
};
A::A(int size){//Конструктор. Создание, инициализация массива.
this->size=size;
a = new int [size];
for (int i = 0; i < size; i++)
a [i] = i + 1;
}
A::A(const A &aA){//Конструктор копирования
size = aA.size;
a = new int [size];
for(int i = 0; i<size; i++)
a[i] = aA.a[i];
}
A A::operator-- (){ //Перегруженный оператор -- префиксной формы
//размер массива уменьшается на единицу
size-=1;
int *b=new int [size];
for (int i=0;i<size;i++){
b[i]=a[i];
}
delete [] a;
a=b;
return *this; //возвращаем объект генерирующий вызов
}
A A::operator++(){ //Перегруженный оператор ++
//размер массива увеличивается на единицу
size+=1;
int *b=new int [size];
for (int i=0;i<size-1;i++){
b[i]=a[i];
}
b[size-1]=size;//заполним новый элемент массива
delete [] a;
a=b;
return *this; //возвращаем объект генерирующий вызов
}
A A::operator+(const A &aA){
size = aA.Get()+this->size;
A temp(size);
return temp;
}
A A::operator=(const A &aA){
size = aA.size;
//cout<<"aA.size: "<<aA.size<<"\n";
a = new int [size];
for(int i = 0; i<size; i++)
a[i] = aA.a[i];
return *this; //возвращаем объект генерирующий вызов
}
int main() {
int i;
A object(12);
cout<<"\nShow:\n";
for(i=0;i<object.Get();i++){
cout<<object[i]<<" ";
}
cout<<"\n\n";
--object;
--object;
--object;
--object;//удалить один элемент массива
cout<<object.Get()<<"\n";
for(i=0;i<object.Get();i++){
cout<<object[i]<<" ";
}
cout<<"\n\n";
++object;
++object;//добавить один элемент массива
cout<<object.Get()<<"\n";
for(i=0;i<object.Get();i++){
cout<<object[i]<<" ";
}
cout<<"\n\n";
A C(0);
A B(5);
cout<<"object.Get = "<<object.Get()<<" B.Get = "<<B.Get()<<"\n";
C=object+B;
for(i=0;i<C.Get();i++){
cout<<C[i]<<" ";
}
cout<<"\n\n";
return (EXIT_SUCCESS);
};
Перегрузка оператора ->
Мы надеемся, что вы помните, что в C++ можно перегрузить почти все операторы, за исключением нескольких. Во всяком случае, оператор → перегружается, и это имеет значение крайне важное. Кстати, этот оператор называется селектором (member selector). Рассмотрим пример:
#include <iostream>
using namespace std;
// класс, указатель на который
// будет инкапсулирован
class Temp
{
int TEMP;
public:
//конструктор
Temp(){TEMP=25;}
//функция показа на экран
void TempFunction(){
cout<<"TEMP = "<<TEMP<<"\n\n";
}
//функция установки значения
void TempSet(int T){
TEMP=T;
}
};
// класс, инкапсулирующий указатель
class MyPtr
{
//указатель на класс Temp
Temp*ptr;
public:
//конструктор
MyPtr(Temp*p=NULL){
ptr=p;
}
// Оператор преобразования типа
// от инкапсулированного к инкапсулирующему
operator Temp*(){
return ptr;
};
// Оператор селектора ->
// который позволит обратиться
// напрямую к "спрятанному"
// указателю
Temp* operator->(){
return ptr;
}
//оператор ++ для смещения указателя вперед
MyPtr operator++(){
ptr++;
return *this;
}
};
void main ()
{
//создание нового объекта
Temp*main_ptr = new Temp;
//простое обращение к членам
//объекта через "родной" указатель
main_ptr->TempFunction();
//создание объекта класса-указателя
MyPtr pTemp(main_ptr);
//обращение через класс-указатель
pTemp->TempFunction();
//создание массива объектов
//инкапсулируемого класса
Temp*arr_=new Temp[3];
//заполнение вышеозначенного массива
//значениями от 0 до 2
for(int i=0;i<3;i++) arr_[i].TempSet(i);
//создание объекта класса указателя
//и запись в него адреса массива
//(здесь работает преобразование типа)
MyPtr arr_temp=arr_;
//сдвиг на один элемент вперед
arr_temp++;
//демонстрация результата
arr_temp->TempFunction();
//удаление объектов
delete main_ptr;
delete[]arr_;
}
Результат работы программы
TEMP = 25
TEMP = 25
TEMP = 1
Итак, обсудим результат: У нас есть класс Temp, который может иметь экземпляры, обладает некоторым членом TEMP и минимальным набором функций для работы с ним. Кроме того, мы создали класс объекта-указателя MyPtr, в котором храним обычный указатель, но доступ к нему ограничиваем, и перегружаем для него операторы:
Оператор приведения типа от Temp к MyPtr Оператор → (selector). Оператор ++, реализующий сдвиг указателя на один шаг вперед. Рассмотрим положительные моменты - мы получили класс объектов-указателей, которые можно смело применять вместо настоящих. Это удобно, т.к. все функции для работы с указателем можно инкапсулировать в этом классе. Однако есть еще одно широко распространенное применение данной конструкции, с которым мы с вами познакомимся в следующем разделе урока. Вперед!
📌 Удобный подбор VPS по параметрам доступен на DIEGfinder.com - официальном инструменте проекта DIEG. Это часть единой экосистемы, созданной для того, чтобы помочь быстро найти подходящий VPS/VDS сервер для любых задач хостинга.
📌 Для тестирования скриптов, установщиков VPN и Python-ботов рекомендуем использовать надежные VPS на короткий срок. Подробнее о быстрой аренде VPS для экспериментов - читайте здесь.
💥 Подпишись в Телеграм 💥 и задай вопрос по сайтам и хостингам бесплатно!7 Самых Популярных Статей
- Как запустить скрипты и веб-приложения на Python
- Что такое страны TIER 1,2,3
- 7 способов сравнения файлов по содержимому в Windows или Linux
- Установка и тестирование веб-панели HestiaCP
- Nginx простые примеры конфигурации
- top, htop, atop определение загрузки ОС (Load average, LA)
- Использование rsync в примерах
7 Самых Популярных Обзоров
- Хостинг для Python-скриптов и приложений
- ТОП 4 лучших антидетект браузеров (Бесплатные & Платные)
- Подборка купонов (промокоды) на хостинг, антидетект браузеры
- Обзор THE.Hosting (PQ Hosting): надежный хостинг с профессиональной поддержкой
- Хостинг в России
- Хостинг в Европе
- Обзор браузера Dolphin {anty} для мультиаккаунтинга