Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Лекция_04.pdf
Скачиваний:
7
Добавлен:
10.02.2015
Размер:
182.57 Кб
Скачать

Пусть для обозначения перегружаемого для класса оператора выбрано общее условное обозначение ##. Для реализации этого оператора следует создатьоператорную функцию с именем, состоящим из ключевого словаoperator и суффикса ##. Она определит преобразование, которое будет выполняться над операндами оператора##. Если речь идет о перегрузке оператора для объектов некоторого класса, то подразумевается, что как минимум один из операндов должен иметь тип объекта данного класса либо являться ссылкой на объект данного класса.

Если необходимо (в бинарных операторах), чтобы один и тот же оператор можно было использовать с различными типами операндов или различным порядком их следования, то для этого определяются несколько операторных функций с одним и тем же, именем отличающихся списками формальных аргументов.

Пусть, например, необходимо определить для объектов класса вещественной матрицы операцию сложения, причем одним из операндов может быть также и вещественное число. То есть, учитывая коммутативность сложения, необходимо обеспечить возможность вычисления операций следующего вида(M, M1 и M2 – объекты класса матрицы, n – вещественная переменная):

M1 + M2 M + n n + M

Для каждой из приведенных операций потребуется создать отдельную перегруженную операторную функцию.

Вызов операторной функции как оператора для объектов классов является всего лишь сокращенной формой явного вызова этой функции. Например:

void f(const Complex cza, const Complex czb)

{

 

 

Complex z1, z2;

 

z1

= cza + czb;

// Сокращенная форма явного вызова.

z2

= cza.operator+(czb); // Явный вызов, если оператор – член класса.

z2

= operator+(cza, czb); // То же, если оператор – не член классов.

 

 

 

}

 

 

 

 

 

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

Операторные функции имеют очень важную

особеннос, вытьекающую из их

предназначения – каждая операторная функциядолжна

возвращать результатв виде

значения (объекта, ссылки на объект) некоторого типа. Тип возвращаемого значения зависит от конкретного оператора и, как правило, типичен для каждого оператора. Так, например, результатом логической операции и операции сравнения всегда будет значение типаbool, а результатом любого оператора с присваиванием всегда будет ссылка на объект– левый

операнд операции. Для некоторых операций тип результата может быть различным и зависеть от сочетания типов операндов.

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

класса, другие должны быть внешними, возможно дружественными к классу,

для

остальных возможен и тот, и другой вариант.

 

4.2. Общие особенности перегрузки бинарных и унарных операторов

Бинарные операторы

 

Если

бинарный оператор перегружается внешней операторной функцией, то

эта

функция

должна иметь два формальных аргумента, причем левый операнд будет

всегда

передаваться ей в качестве первого аргумента, а правый - в качестве второго.

Пусть АА и СС - некоторые классы, а ## - обозначение бинарного оператора, перегружаемого для объектов классаAA. Если в программе определена внешняя операторная функция вида:

CC operator##(AA al, AA ar)

{

// Тело ...

}

то в тексте программы программист может заменить ее явный вызов

operator##(al, ar);

привычным выражением al ## ar.

Если операторная функция является членом класса АА, то левый операнд ее бинарного оператора будет объектом ее класса-владельца, а правый - ее единственным формальным аргументом. Поэтому присутствие в классе АА функции-члена

CC AA::operator##(AA ar)

{

// Тело ...

}

позволяет программисту заменить в тексте программы ее явный вызов

al.operator##(ar);

привычным выражением al ## ar.

Унарные префиксные операторы

 

 

 

Если унарный

оператор

перегружается внешней

операторной

функцией, то

единственный ее формальный аргумент исполняет роль операнда этого оператора.

Пусть АА и СС -

классы, а ## -

обозначение унарного префиксного оператора. Если в

программе определена внешняя операторная функция вида

CC operator##(AA aa)

{

// Тело ...

}

то в тексте программы программист может заменить ее явный вызов

operator##(aa);

привычным выражением ##аа.

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

CC AA::operator##( )

{

// Тело ...

}

позволяет программисту заменить в тексте программы ее явный вызов

aa.operator##();

привычным выражением ##аа.

Унарные постфиксные операторы

При перегрузке постфиксного унарного оператора в его операторную функцию следует добавлять дополнительный формальный аргумент типаint (фиктивный аргумент). Он нужен компилятору только для распознавания постфиксной формы унарного оператора.

Если внешняя операторная функция унарного оператора будет иметь вид:

CC operator##(AA aa, int)

{

// Тело ...

}

то в тексте программы программист может заменить ее явный вызов

operator## (aa, num);

привычным выражением аа##. Здесь num – аргумент, значение которого в функции не используется (фиктивный аргумент).

Поэтому обычно имя формального аргумента не задается, а фактический аргумент при явном вызове операторной функции может быть произвольным, но должен иметь тип int.

Если операторная функция-член класса для унарного оператора имеет вид:

CC AA::operator##( int )

{

// Тело ...

}

то в тексте программы программист может заменить ее явный вызов

aa.operator##(num);

тем же выражением аа##.

Полезно будет отметить, что операторные функции-члены класса всегда должны быть

нестатическими1.

Правила выбора места определения операторных функций

Общим правилом можно считать следующее. Следует включать в определение класса (т.е. перегружать операторы в форме функций-членов класса) те операторы, которые модифицируют значение своего операнда, такие как ++. Операторы, которые выдают новый объект класса на основе своих операндов, такие как бинарный +, либо не изменяют содержимое операндов, такие как ==, << (в качестве оператора вывода), следует определять вне класса. Они могут быть дружественными или недружественными к классу. При их реализации, как правило, используют уже имеющиеся операторные функции-члены для других операторов.

Из приведенного общего правила есть достаточно много исключений. Рассмотрим их. Для перегрузки оператора = (присваивание), составных операторов присваивания (+=, *=

и т. д.), оператора [] (индексация), оператора () (вызов функции) и оператора -> (выбор элемента или метода через указатель) можно использовать толькофункции-члены класса. Это гарантирует, что их первый операнд будет lvalue (ссылкой на объект класса либо самим объектом).

Для перегрузки операторов преобразования объекта класса к другим типам(операторы преобразования типа) также необходимо применять только функции-члены этого класса.

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

Далее в лекции на примере простого класса комплексных чисел(class Complex) будут

1 О понятии статических членов класса см. предыдущую лекцию

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