OOP / Лекция 4
.pdf// 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];}
41
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’, ‘##’, ‘#’, ‘.*’), нельзя вводить новые знаки операций, нельзя изменять приоритеты операций, для некоторых операций (‘=’,’[]’,’->’) операцию-функцию можно определять только как нестатическую компонентную функцию класса.
