Для перегрузки встроенных операторов С ++ можно использовать ключевое слово operator. Как и имени функции – типа print – ему может быть присвоен ряд значений, зависящих от параметров. Таким образом, опе-ратору типа + также можно присваивать дополнительные значения. Перегрузка операторов позволяет АТД использовать синтаксис выражений C++. Это облегчает написание программ и делает их более читабельными. Функция тру из предыдущего раздела могла быть написана как
vect operator* (const vect& v, const matrix& m)
где * является двухместным оператором умножения. После этого, если г и s были vect, a t была matrix, функцию умножения будет вызывать естественное выражение
r = s * t;
Это заменит запись функции r = mpy(s,t);
Перегруженный оператор можно вызывать и с использованием функциональной формы записи:
r = operator*(s,t);
Хотя операторам и могут добавляться новые значения, их ассоциативность и приоритет остаются прежними. Например, оператор умножения сохраняет более высокий приоритет, чем оператор сложения. (Таблица приоритетов операторов для C++ включена в приложение В.) Перегружены могут быть почти все операторы. Имеются следующие исключения: оператор членства “.”, оператор селектора члена объекта “. *” (см. главу 8), троичный условный оператор выражения “? : “, оператор “sizeof” и оператор разрешения контекста
Доступны все арифметические, логические операторы, операторы сравнения, равенство, присвоение, операторы поразрядных операций, префиксные и постфиксные формы операторов приращения и декремента. Могут быть перегружены оператор индексации “[ ]” и обращение к функции “()”. Также могут быть перегружены оператор указателя класса “->” и оператор указателя на член “->*” (см. главу 8). Возможна перегрузка new и- delete.
Операторы иногда нуждаются также в доступе к скрытым полям более чем одного класса. Для этого их можно сделать дружественными функциями.
Перегрузка унарного оператора
Продолжим рассмотрение перегрузки оператора, проиллюстрировав ее перегрузкой одноместных операторов, типа “!”, “++”, “~” и “[]“. Для этих целей разработаем класс clock, который может быть использован для хранения времени в виде дней, часов, минут и секунд. Будем развивать хорошо знакомые операции для clock:
class clock
{private:
unsigned long int tot_sees, sees, mins, hours, days;
public:
clock(unsigned long int i); //конструктор и преобразование
void print(); //форматированный вывод
void tick(); //добавление одной секунды
clock operator++(){ this->tick(); return (*this); }
};
Этот класс перегружает префиксный оператор приращения. Сигнатурой для перегрузки совместимого постфиксного оператора приращения будет
lock operator++(int). После перегрузки эта форма оператора приращения неявно вызывается с целым значением 0 в качестве реального целого параметра. Перегруженный оператор представляет собой функцию-член и может быть вызван со своим аргументом по умолчанию. Функция-член tlick добавляет одну секунду к неявному аргументу перегруженного оператора++.
inline clock::clock (unsigned long int i)
{tot secs = i;
sees = tot sees % 60;
mins = (tot_secs / 60) % 60;
hours = (tot_secs / 3600) %60;
days = tot_secs / 86400;
}
void clock::tick()
{clock temp (++tot sees) ;
sees = temp.sees;
mins = temp.mons;
hours = temp.hours;
days = temp.days;
}
Конструктор выполняет удобное преобразование из tot_secs в дни, часы, минуты и секунды. Например, поскольку в дне 86400 секунд, деление нацело на эту константу дает число дней. функция-член tick создает clock temp, который добавляет одну секунду к общему времени. Конструктор действует как функция преобразования, которая, собственно, и обновляет время.
Перегруженный operator++() также обновляет неявную переменную clock и возвращает обновленное значение. Он может быт написан также, как и tick (), за исключением оператора
return (temp).
void clock::print ()
(cout<
Результат вывода программы:
initial times are Od:0h :0m :59s 1 d :23 h :59 m :59 s after one second times are Od:0h :lm:0s 2d:0h:0m:0s
Префиксную операцию ++ можно перегрузить следующим образом, используя friend-функцию
friend clock operator++(clocks c1)
{ c1.tick(); return (c1); }
Заметьте, что, поскольку переменная clock должна увеличиться на одну секунду, мы вызываем ее по ссылке. Решение о выборе между представлением friend и функцией-членом обычно зависит от того, насколько необходимы и доступны операторы неявного преобразования. Явная передача аргумента, как в friend-функции, позволяет автоматическое его приведение.
Перегрузка бинарного оператора
Продолжаем пример clock, демонстрируя перегрузку двухместных операторов. В основном, сохраняются те же принципы. Если двухместный оператор перегружается с использованием функции-члена, то в качестве своего первого аргумента он получает неявно переданную переменную класса, а второго - единственный из списка аргументов. При объявлении friend-функции или обычной функции в списке параметров определяют оба аргумента. Конечно, обычная функция не может обращаться к private членам.
Напишем операцию сложения для типа clock.
class clock
{
friend clock operator+(const clock& cl,const clock& c2);
}
clock operator+(const clock& c1, const clock& c2)
{clock temp(c1.tot sees + c2.tot sees);
return (temp) ;
};
Оба параметра явно определяются и являются кандидатами для преобразования назначением. Строка программы
clock temp (c1.tot_secs + c2.tot secs);
использует конструктор для преобразования выражения unsigned long int в значение clock.
Используя это определение, имеем
int i = 5;
clock с(900) ;
...
с + i //допустимо: i преобразовано в clock
i + с //допустимо: i преобразовано в clock
В противоположность этому, перегрузим двухместный минус функцией-членом:
class clock
{
clock operator-(const clock& с);
};
clock clock::operator-(const clock& c)
{
clock temp( tot_secs - c.tot_secs);
return (temp) ;
}
Помните, что существует неявный первый аргумент. В него попадает некоторый используемый параметр. Это также может вызывать асимметричное поведение двухместных операторов.
int i = 5;
clock с(900) ;
c - i //допустимо: i преобразовано в clock
с.operator-(i) //вызов функции
i - с //недопустимо: i не относится к типу clock i.operator-(с) //недопустимая запись вызова функции
Ясно видно, что при использовании вызова функции, переменная i не относится к типу clock и, следовательно, не "понимает" значение минуса.
Определим операцию умножения как двухместную операцию с первым параметром unsigned long int и вторым параметром - переменной clock. Операция реализуется как friend-функция.
clock operator*(unsigned long int m,const clock& c)
{
clock temp(m * с.tot secs);
return (temp);
}
Такая реализация вынуждает операцию умножения иметь фиксированный порядок выполнения, зависящий от типа. Для избежания этого обычно пишется второй перегруженный функциональный оператор
clock operator*(const clock& с, unsigned long int m);
Похожие записи
No user прокомментировали сообщение
Оставить комментарий