Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
Перегрузка операций абстрактного типа Vect
Для того, чтобы переопределить одну из стандартных операций языка С++ для работы с операндами абстрактных типов, программист должен написать функцию с именем operator@ где @ - оператор языка С++. Например, для объектов класса Vect переопределены (перегружены) как унарные (-,! ), так и бинарные (-, +, [], =) операторы. Перегрузка операторов не изменяет их ассоциативность и приоритет. Если операторная функция с именем operator@ реализована как функция-элемент класса, то в программе пользователя возможны различные варианты ее вызова. Например, унарные операторы могут вызываться в префиксной или функциональной формах: x=@a; x=a.operator@(); В этом случае единственный аргумент функции передается в функцию как неявный аргумент - указатель this на текущий объект (в данном примере на объект с именем a). Бинарные операторы могут вызываться в инфиксной и функциональной формах: y=a@b; y=a.operator@(b); Здесь первый аргумент передается в операторную функцию в неявном виде. Недостаток использования функций-элементов класса для реализации операторных функций заключается в том, что здесь не выполняется преобразование типа для первого или единственного (в случае унарного оператора) аргумента. То есть в качестве первого аргумента необходимо указывать имя объекта класса, для которого перегружена данная операторная функция. Это не соответствует действиям, предусмотренным для операций над предопределенными в языке С++ типами данных.Для устранения этого недостатка используются дружественные операторные функции. Если операторная функция с именем operator@ реализована как дружественная классу функция, то в программе пользователя также возможны различные варианты ее вызова: x=@a; // Унарный x=operator@(a); // оператор y=a@b; //Бинарный y=operator@(a, b); //оператор Для данных типа Vect операции - (унарная и бинарная), =, [] реализованы с помощью операторных функций-элементов класса, а операции + и! реализованы как дружественные классу Vect операторные функции. Унарный - для объектов класса Vect - это операция изменения знака у всех элементов массива, связанного с данным объектом:
Vect Vect:: operator-() { Vect Temp(size); for(int i=0; i< size; ++i) Temp.p[i]=-p[i]; return Temp; }
Отметим, что не все объектно-ориентированные языки позволяют пользователям перегружать операторы в абстрактных (пользовательских) типах. Например, C++ и C# предоставляют такую возможность, а Java – нет. Поэтому для повышения мобильности создаваемых типов необходимо перегрузку каждой операции выполнять дважды – с помощью операторной функции и с помощью обычного метода. В классе Vect таким образом перегружаются бинарные операции " -" и " +". Необходимо также отметить, что операции сравнения объектов должны перегружаться парами: > и < , > = и < =, == и! =. Бинарный " -" для объектов класса Vect - это операция поэлементного вычитание массивов, входящих в объекты:
Vect Vect:: subtract(const Vect& v) { int s=(size< v.size)? size: v.size; Vect temp(s); if(v.size! =size) cerr< < " Invalidate array sizes: " < < size< < " and " < < v.size< < endl; for(int i=0; i< s; ++i) temp.p[i]=p[i]-v.p[i]; return temp; } Vect Vect:: operator-(const Vect& v) { return subtract(v); }
Дружественная функция operator+() получает все свои аргументы в явном виде и выполняет поэлементное сложение двух объектов (переменных) типа Vect:
Vect plus(const Vect& a, const Vect& b) { int s=(a.size< b.size)? a.size: b.size; Vect sum(s); if(a.size! =b.size) cerr< < " Invalidate array sizes: " < < a.size< < " and " < < b.size< < endl; for(int i=0; i< s; ++i) sum.p[i]=a.p[i]+b.p[i]; return sum; } Vect operator+(const Vect& a, const Vect& b) { return plus(a, b); }
Унарный! рассматривает целочисленный массив, связанный с объектом, как булевский и выполняет поэлементное инвертирование его значений, то есть нулевые элементы заменяются единицами, а ненулевые элементы - нулями:
Vect operator! (const Vect& v) { Vect temp(v.size); for(int i=0; i< v.size; ++i) if(v.p[i]) temp.p[i]=0; else temp.p[i]=1; return temp; } Переопределение операций для абстрактных типов имеет ряд ограничений. Так, операторные функции operator[]() и operator=() обязательно должны быть элементами класса. Если x - объект абстрактного типа, а y - индексное выражение, то в программе пользователя должны быть допустимы следующие формы использования перегруженного оператора индексации: int a=x[y]; a=x.operator[](y); x[y]=b; x.operator[](y)=b; x[y1]=x[y2]=a; x.operator[](y1)=x.operator[](y2)=a; Отметим, что в программе пользователя индексы элементов массива объекта типа Vect изменяются от 1 до size, в то время как реально они изменяются от 0 до size-1:
int& Vect:: operator[](int i) { if(i< 1 || i> size){ cerr< < " Invalidate array size: " < < i< < endl; exit(1); } return p[i-1]; }
Необходимо различать случаи использования стандартного и перегруженного операторов [], например: Vect a[20]; a[3][5]=6; Здесь а[3][5] интерпретируется как (a[3]).operator[](5). Первая из двух операций [] является стандартной, так как выполняется над именем массива (совершенно неважно, какой тип имеют элементы), а вторая - переопределенной, так как результатом первой операции [] является объект типа Vect. При создании нового типа данных можно не перегружать оператор присваивания. В этом случае компилятор С++ выполняет операцию присваивания по умолчанию, которая заключается в рекурсивном почленном копировании одного объекта в другой. Например, после выполнения операторов Vect arr1, arr2; arr1=arr2; arr2[5]=13; cout< < " arr1[5]=" < < arr1[5]< < endl; выведенное значение элемента аrr1[5] будет равно 13. Это обусловлено тем, что при почленном копировании поля данных объекта, стоящего справа от оператора присваивания, будут скопированы в поля данных объекта, стоящего слева от оператора присваивания. То есть адрес, заданный полем p в объекте arr2, будет занесен в поле p объекта arr1. Другими словами, оба указателя будут указывать на один и тот же массив целых чисел. Поэтому для новых типов данных обычно определяется операторная функция operator=(), которая будет выполнять операцию присваивания с учетом всех тонкостей реализации нового типа данных:
Vect& Vect:: operator=(const Vect& v) { int s=(size< v.size)? size: v.size; if(v.size! =size) cerr< < " Invalidate array sizes: " < < size< < " and " < < v.size< < endl; for(int i=0; i< s; ++i) p[i]=v.p[i]; return (*this); }
Необходимо отличать оператор присваивания от оператора инициализации. Во втором случае создается и инициализируется новый объект:
Vect a, b, c; b=! c; a=b; //присваивание Vect d=b; // инициализация
Здесь для создания и инициализации объекта d будет вызван конструктор копии. Операция вывода объекта класса Vect на экран в определенном текстовом формате реализуется посредством операторной friend-функции:
ostream& operator< < (ostream& s, Vect& v) { s< < endl; for(int i=1; i< =v.size; i++) s< < v[i]< < " "; s< < endl; return s; }
Реализацию абстрактного типа целесообразно выполнять посредством использования двух файлов (модулей): файла-заголовка с расширением.h для определения абстрактного типа данных и основного файла с расширением.cpp для определения функций, реализующих операции над типом данных. Программа, использующая абстрактный тип Vect, показана на рис.4. Таким образом, при использовании среды MS Visual Studio.NET, в проект приложения необходимо включить файлы Vect.h, Vect.cpp и Vectprog.cpp.
#include < iostream> #include < iomanip> #include " Vect.h" using namespace std; void main() { int i, arr[]={1, 2, 3, 4, 5, 6, 7, 8, 9, 10}; Vect a, b, c, d(10); a[1]=a[5]=a[10]=5; cout< < " a="; for(i=1; i< =10; i++) cout< < setw(4)< < a[i]; cout< < endl; b=! a; cout< < " b="; for(i=1; i< =10; i++) cout< < setw(4)< < b[i]; cout< < endl; c=-a; d=b+c; Vect e=d; Vect f(e); Vect g(arr, 10); e=g-f; cout< < " e="; for(i=1; i< =10; i++) cout< < setw(4)< < e[i]; cout< < endl; }
Рис.4. Программа Vectprog.cpp
Популярное:
|
Последнее изменение этой страницы: 2016-05-03; Просмотров: 683; Нарушение авторского права страницы