Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
posobie1.doc
Скачиваний:
13
Добавлен:
01.05.2019
Размер:
457.22 Кб
Скачать

3.7. Перегрузка операций

Классы вводят в программу новые пользовательские типы данных. Такие типы могут использоваться наравне со стандартными ( базовыми): они могут использоваться для определения новых переменных, могут входить в списки параметров функций и определять тип возвращаемого значения. В условиях фактического равноправия производных и основных типов данных должна существовать возможность сохранения привычной структуры выражений при работе с данными производных типов. Это означает, что выражение для вычисления суммы двух слагаемых уже известного нам типа array по своей структуре не должно отличаться от соответствующих выражений для слагаемых типа int или float. Но большинство операций языка C++ определены лишь для основных типов данных. Использование в качестве операндов операций выражений производных типов вызывает ошибки трансляции.

int a,b,c;

c=a+b; // использование операндов базовых типов для операции сложения разрешено

array m1(4),m2(4),m3(4);

// m1=m2+m3; ошибка!!! использование операндов производных типов для

//операции сложения запрещено

И всё же возможность сохранения привычной структуры выражений для производных типов в C++ существует. Для этого в программе необходимо перегрузить операцию для нестандартного типа данных. Перегрузка операции заключается в определении специальной функции, которая будет вызываться при использовании операции языка С++ с нестандартными типами данных.

Функция, перегружающая операцию в С++ называется «операция-функция» и должна выглядеть следующим образом:

тип operator знак_операции (список формальных_параметров)

{тело_функции

}

Здесь тип – тип возвращаемого функцией значения, operator- ключевое слово, определяющее, что данная функция перегружает операцию, знак_операции – знак той операции языка С++, которую перегружает данная функция. Количество параметров функции определяется количеством операндов у соответствующей операции, а также способом определения функции. Операция-функция может быть как компонентной функцией некоторого класса, так и внешней функцией. Рассмотрим пример перегрузки операций для класса array2 (двумерный массив целых чисел).

//Листинг 18. Перегрузка операций для класса «двумерный массив целых чисел»

#include <iostream.h>

#include <conio.h>

class array2

{ int **mas; //указатель на массив

int n,m; //количество строк и столбцов

public:

array2(int,int); //конструктор

void ReadMas(); //ввод массива с клавиатуры

void WriteMas(); //вывод элементов массива на экран

~array2(); //деструктор

operator int(); //перегрузка операции приведения типа

friend array2& operator*(array2&,array2&); //дружественная функция перегрузки

//операции умножения

void operator=(array2&); //перегрузка операции присвоения

};

array2::array2(int a,int b){n=a;m=b; //динамически выделяем память под двумерный массив

mas=new int *[n];

for (int i=0;i<n;i++)

mas[i]=new int[m];

}

void array2::ReadMas(){

cout<<"Введите массив \n";

for (int i=0;i<n;i++)

for (int j=0;j<m;j++)

cin>>mas[i][j];}

void array2::WriteMas() {

for (int i=0;i<n;i++)

{ for (int j=0;j<m;j++)

cout<<mas[i][j]<<'\t';

cout<<'\n';

} }

array2::~array2(){for (int i=0;i<n;i++)

delete [] mas[i];

delete[]mas;

}

array2& operator*(array2& m1,array2& m2) //внешняя функция перегрузки операции //умножения

{ if(m1.m==m2.n)

{array2 *pta;

int s;

pta=new array2(m1.n,m2.m);

for(int i=0;i<m1.n;i++)

{ for(int j=0;j<m2.m;j++)

{s=0;

for(int k=0;k<m1.m;k++)

s+=m1.mas[i][k]*m2.mas[k][j];

pta->mas[i][j]=s;

}

}

return *pta;

}

else cout<<"Error";

}

array2::operator int()

{ int sum=0;

for(int i=0;i<n;i++)

for(int j=0;j<m;j++)

sum+=mas[i][j];

return sum;

}

void array2::operator=(array2& m2)

{ if(n==m2.n&&m==m2.m)

for(int i=0;i<n;i++)

for(int j=0;j<m;j++)

mas[i][j]=m2.mas[i][j];

else cout<<"Error";

}

main()

{ array2 m(2,3),m1(3,4),m2(2,4);

m.ReadMas();

m1.ReadMas();

m2=m*m1; //перемножаем массивы по правилу перемножения матриц

m2.WriteMas();

int c=int(m2); //получаем сумму элементов массива

cout<<c;

getch();

}

В приведенном примере для класса «двумерный массив целых чисел» перегружены три операции: операция умножения ‘*’, выполняющая умножение массива по правилам перемножения матриц, операция приведения типа ‘int()’, которая применительно к массиву вычисляет сумму элементов массива, а также операция присвоения ‘=’, которая поэлементно копирует содержимое одного массива в другой. Рассмотрим подробнее операцию-функцию

operator * . Данная операция-функция определена как внешняя по отношению к классу функция с правами друга, так как она работает с частными компонентами класса n, m, mas. Функция имеет два параметра – это операнды операции умножения. После такого объявления функции любое использование в программе выражения типа a*b при условии, что а и b являются объектами класса array2, приводит к вызову operator *(a,b). Подобный вызов операции-функции можно непосредственно поместить в текст программы, но он уступает в понятности и наглядности использованию обычной операции умножения. Таким образом, при определении операции-функции как внешней функции количество ее параметров должно совпадать с арностью перегружаемой операции, поскольку операнды перегруженной операции становятся фактическими параметрами соответствующей операции-функции.

Для перегрузки операции приведения типа ‘int()’ определена операция-функция как метод класса array2. При подобном определении один из операндов операции становится тем объектом, для которого вызывается операция-функция, а остальные операнды (если они есть) передаются как фактические параметры. Таким образом, для вызова операции приведения типа с параметром – объектом класса array2 в последнем примере:

int(m2)

будет вызвана операция-функция в следующем варианте:

m2.operator int()

Если бы операция умножения была перегружена методом класса array2 , а не внешней функцией, то для нее необходимо было бы определить лишь один формальный параметр – через него в функцию передавался бы лишь второй операнд операции умножения. Первый операнд в этом случае становится тем объектом, для которого вызывается операция функция:

//Листинг 19. Определение операции-функции как метода класса

class array2

{ …

array2& operator*(array2& m2) //функция перегрузки операции

//умножения как метод класса

{ if(m==m2.n)

{array2 *pta;

int s;

pta=new array2(n,m2.m);

for(int i=0;i<n;i++)

{ for(int j=0;j<m2.m;j++)

{s=0;

for(int k=0;k<m;k++)

s+=mas[i][k]*m2.mas[k][j];

pta->mas[i][j]=s;

}

}

return *pta;

}

else cout<<"Error

}

};

main()

{ array2 m(2,3),m1(3,4),m2(2,4);

m2=m*m1;//можно записать в виде m2=m.operator *(m1)

}

Перегрузка операции присваивания необходима для класса array2 в связи с тем, что обычное копирование компонентных данных из одного объекта в другой, которое производит операция присваивания по умолчанию, не подходит для поставленной задачи. Если написать выражение m1=m2, не переопределяя операцию присваивания, то компонент mas одного объекта будет скопирован в компонент mas другого, что приведет к использованию обоими объектами одного и того же динамического массива в дальнейшем. Переопределив же операцию присваивания, мы при присваивании объектов копируем элементы одного динамического массива в другой.

При перегрузке операций существует ряд ограничений: нельзя перегружать некоторые операции (‘.’, ’?:’, ’::’, ‘sizeof’, ‘##’, ‘#’, ‘.*’), нельзя вводить новые знаки операций, нельзя изменять приоритеты операций, для некоторых операций (‘=’,’[]’,’->’) операцию-функцию можно определять только как нестатическую компонентную функцию класса.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]