Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Перегрузка операций абстрактного типа 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; Просмотров: 637; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.029 с.)
Главная | Случайная страница | Обратная связь