Инструменты пользователя

Инструменты сайта


peregruzka_operatorov

Различия

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

Ссылка на это сравнение

peregruzka_operatorov [2010/12/18 12:47] (текущий)
Строка 1: Строка 1:
 +====== Перегрузка операторов ======
 +Перегрузка операторов является одной из разновидностью [[полиморфизм]]а.
 +
 +В С++ есть возможность распространения действия стандартных операций на операнды абстрактных типов данных. Для того, чтобы переопределить одну из стандартных операций для работы с операндами абстрактных типов, программист должен написать функцию с именем operator знак, где знак - обозначение этой операции (например,​ + - | += и т.д.). ​
 +
 +Однако в языке существует несколько ограничений,​ накладываемых на переопределение операторов: ​
 +
 +  - Нельзя создавать новые символы операций. ​
 +  - Нельзя переопределять операции:<​file>​
 +::
 +* (разыменование,​ а не бинарное умножение)
 +?: 
 +sizeof
 +## 
 +#
 +.
 +</​file>​
 +  - Cимвол унарной операции не может использоваться для переопределения бинарной операции и наоборот. Например,​ символ << можно использовать только для бинарной операции,​ ! - только для унарной,​ а & - и для унарной,​ и для бинарной.
 +  - Переопределение операций не меняет ни их приоритетов,​ ни порядка их выполнения (слева направо или справа налево).
 +  - При перегрузке операции компьютер не делает никаких предположений о ее свойствах. Это означает,​ что если стандартная операция += может быть выражена через операции + и =, т.е. а + = b эквивалентно а = а + b, то для переопределения операций в общем таких соотношений не существует,​ хотя, конечно,​ программист может их обеспечить. ​
 +  - Никакая операция не может быть переопределена для операндов стандартных типов.
 +  - Как для унарной,​ так и для бинарной операции число аргументов функции operator () должно точно соответствовать числу операндов этой операции. Причем в перегрузку бинарного оператора принято передавать один аргумент,​ так как второй - неявный. Его имеет любая функция - член класса,​ это тот самый [[указатель this]] - указатель на объект,​ для которого вызван метод. Таким образом,​ в переопределение унарного оператора не следует передавать ничего вовсе. ​
 +<​note>​Удобно передавать значения параметров в функцию operator() не по значению,​ а по [[reference|ссылке]].</​note>​
 +====== Пример 1. Перегрузка бинарные операторы +-*% ======
 +<​file>​
 +#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();
 +}
 +</​file>​
 +====== Пример 2. Класс динамического массива. ======
 +**Задача:​** Создайте класс динамического массива,​ в котором реализована проверка выхода за границы массива. Перегрузите операторы:​ [ ], =, +, -,++ (добавление элемента в конец массива),​ – (удаление элемента из конца массива).
 +<​file>​
 +/*
 + * 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);​
 +};
 +</​file>​
 +====== Перегрузка оператора -> ======
 +Мы надеемся,​ что вы помните,​ что в C++ можно перегрузить почти все операторы,​ за исключением нескольких. Во всяком случае,​ оператор -> перегружается,​ и это имеет значение крайне важное. Кстати,​ этот оператор называется селектором (member selector). Рассмотрим пример: ​
 +
 +<​file>​
 +#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
 +</​file>​
 +Итак, обсудим результат:​ У нас есть класс Temp, который может иметь экземпляры,​ обладает некоторым членом TEMP и минимальным набором функций для работы с ним. Кроме того, мы создали класс объекта-указателя MyPtr, в котором храним обычный указатель,​ но доступ к нему ограничиваем,​ и перегружаем для него операторы: ​
 +
 +Оператор приведения типа от Temp к MyPtr 
 +Оператор -> (selector). ​
 +Оператор ++, реализующий сдвиг указателя на один шаг вперед. ​
 +Рассмотрим положительные моменты - мы получили класс объектов-указателей,​ которые можно смело применять вместо настоящих. Это удобно,​ т.к. все функции для работы с указателем можно инкапсулировать в этом классе. Однако есть еще одно широко распространенное применение данной конструкции,​ с которым мы с вами познакомимся в следующем разделе урока. Вперед!
 +
  
peregruzka_operatorov.txt · Последние изменения: 2010/12/18 12:47 (внешнее изменение)