Файл: Pobegaylo_A._C_Cplus_dlya_studenta.pdf

ВУЗ: Не указан

Категория: Не указан

Дисциплина: Не указана

Добавлен: 13.12.2020

Просмотров: 4158

Скачиваний: 28

ВНИМАНИЕ! Если данный файл нарушает Ваши авторские права, то обязательно сообщите нам.
background image

  

 
 

Г Л А В А  

16 

 
 
 

Перегрузка операторов 

 

16.1. Общие правила 

В языке программирования C++ 

операторы

 рассматриваются как 

функции, которые имеют следующий прототип: 

  тип operator имя_оператора(список_параметров); 

Здесь 

operator

  —  ключевое  слово.  Поэтому  операторы,  так  же 

как  и  функции,  можно  перегружать.  Перегружаются  почти  все 
операторы, исключая нижеприведенные случаи: 

 

нельзя перегружать следующие операторы:  

 

.

 — точка, оператор доступа к члену класса; 

 

.*

 — оператор доступа к члену класса через указатель; 

 

::

 — оператор разрешения области видимости; 

 

:?

 — условный оператор; 

 

нельзя перегружать препроцессорные операторы: 

 

#

 — преобразование в строку; 

 

##

 — сцепление строк; 

 

нельзя перегружать операторы 

sizeof

 и 

typeof

 

нельзя перегружать операторы преобразования типов данных: 

static_cast

const_cast

reinterpret_cast

dynamic_cast

  

 


background image

Глава 16. Перегрузка операторов 

207 

Перегрузка операторов должна удовлетворять следующим требо-
ваниям: 

 

нельзя вводить новых операторов; 

 

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

 

нельзя изменять количество параметров оператора; 

 

ассоциативность перегруженных операторов не изменяется; 

 

приоритеты перегруженных операторов не изменяются; 

 

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

()

 

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

()

Перегруженные  операторы  разрешается  определять  как  члены 
класса  и  как  функции  не  члены  класса.  Один  оператор  может 
быть  перегружен  несколько  раз.  Если  оператор  перегружен  как 
член  класса,  то  он  не  должен  иметь  спецификатор 

static

  и  его 

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

Bool

,  определение  которого  приведено  в 

листинге 16.1. 

Листинг 16.1. Определение класса четных чисел 

#include <iostream> 
 
#if !defined BOOL 
#define BOOL 


background image

Часть II. Язык программирования С++ 

208 

class Bool 

  int n; 
public: 
  // конструкторы 
  Bool() { n ? n = 1 : n = 0; } 
  Bool(int _n) : n(_n ? 1 : 0) {} 
  // унарные операторы логического сложения и умножения 
  Bool& operator +=(const Bool& b); 
  Bool& operator *=(const Bool& b); 
  // оператор преобразования типа 
  operator bool(void) const; 
  // унарный логический оператор отрицания 
  friend Bool operator !(const Bool& b); 
  // бинарные операторы логического сложения и умножения 
  friend Bool operator +(const Bool& b1, const Bool& b2); 
  friend Bool operator *(const Bool& b1, const Bool& b2); 
  // бинарные операторы сравнения 
  friend Bool operator ==(const Bool& b1, const Bool& b2); 
  friend Bool operator !=(const Bool& b1, const Bool& b2); 
  // операторы ввода/вывода 
  friend std::istream& operator >>(std::istream& in, Bool& b); 
  friend std::ostream& operator <<(std::ostream& out,  
                                   const Bool& b); 
}; 
 
#endif

 

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

Bool

  можно  прово-

дить следующие операции: 

  Bool a(0), b(1), c(0); 
  c.operator +=(a);      // явный вызов оператора += 


background image

Глава 16. Перегрузка операторов 

209 

  c += b;                // неявный вызов оператора += 
  c = operator +(a, b);  // явный вызов оператора + 
  c = a + b;             // неявный вызов оператора + 

16.2. Унарные операторы 

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

  тип operator @(); 

или как операторы (не члены класса) с одним параметром: 

  тип operator @(параметр); 

Здесь 

@

 обозначает один из следующих унарных операторов: 

  &  *  +  -  ~  ! 

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

Bool

  Bool& operator !() { n ? n = 0 : n = 1; return *this; } 
  Bool operator !() const { return Bool(n ? 0 : 1); } 

Выбираем  второй  вариант,  так  как  он  не  изменяет  логическое 
значение объекта, к которому применяется оператор 

!

. Это согла-

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

  Bool operator !(const Bool& b) { return Bool(b.n ? 0 : 1); } 

Такая  реализация  более  предпочтительна,  чем  реализация  унар-
ного отрицания как члена класса, так как она позволяет преобра-
зование типа параметра 

b

. Эту реализацию и оставляем для клас-

са 

Bool

. Такой же подход используем и для остальных префикс-

ных унарных операторов. 
После  перегрузки  унарного  отрицания  над  логическими  значе-
ниями можно выполнять следующие операции: 


background image

Часть II. Язык программирования С++ 

210 

  Bool a(0), b(0); 
  a = !b;  // a = 1 

Можно  оставить  два  определения  префиксного  унарного  опера-
тора:  как  члена  и  как  друга  класса.  В  этом  случае  при  вызове 
унарного оператора компилятор выбирает тот вариант, для кото-
рого преобразования типа считается наилучшим. 

16.3. Оператор присваивания 

Оператор  присваивания  может  быть  перегружен  только  как  не-
статический член класса. Перегруженный оператор присваивания 
должен иметь следующий прототип: 

  имя_класса& operator =(const имя_класса&); 

При  реализации  оператора  присваивания  должна  проверяться 
возможность  присваивания  объекта  самому  себе.  Например,  пе-
регрузим оператор присваивания для класса 

Bool

  Bool& operator =(const Bool& b) 
  { 
    if (&b != this) 
      n = b.n; 
    return *this; 
  } 

Теперь  можно  выполнять  присваивание  логических  значений, 
например: 

  Bool a(0), b(1); 
  a = b;  // a = 1 

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

Bool

  оператор  присваивания  выполняет  только  почленное 

копирование атрибутов, то его определение можно опустить. 


Смотрите также файлы