Шаг 16.
Перегрузка стандартных операций

    С этого шага мы начнем знакомиться с механизмом перегрузки стандартных операций.

    Одной из особенностей языка C++ является возможность распространения действия стандартных операций на операнды, для которых эти операции первоначально в языке не предполагались. Например, если S1 и S2 - символьные строки, то их конкатенацию (соединение) удобно было бы обозначить как S1 + S2. Однако бинарная операция "+" в обычном контексте языка C++ предназначена для арифметических операндов и не предусматривает строковых операндов. Никакой возможности распространить действие стандартной операции "+" на строки в виде символьных массивов или строковых констант в языке C++ нет.

    Однако, если определить S1 и S2 как объекты некоторого класса, например, введенного на 6 шаге класса stroka, то для них можно ввести операцию "+", выполняемую по таким правилам, которые заранее выбрал программист.

    Для этих целей язык C++ позволяет распространить действие любой стандартной операции на новые типы данных, вводимые пользователем. Распространить операцию на новые типы данных позволяет механизм перегрузки стандартных операций.

    Чтобы появилась возможность использовать стандартную для языка C++ операцию (например, "+" или "*") с необычными для нее данными, необходимо специальным образом определить ее новое поведение. Это возможно, если хотя бы один из операндов является объектом некоторого класса, т.е. введенного пользователем типа. В этом случае применяется механизм, во многом схожий с механизмом определения функций.

    Для распространения действия операции на новые пользовательские типы данных программист определяет специальную функцию, называемую "операция-функция" (Operator Function). Формат определения операции-функции:

  тип_возвращаемого_значения operator знак_операции
                   (спецификация_параметров_операции-функции) 
         { операторы_тела_операции-функции }

    При необходимости может добавляться и прототип операции-функции с таким форматом:

  тип_возвращаемого_значения operator знак_операции
                   (спецификация_параметров_операции-функции);

    И в прототипе, и в заголовке определения операции-функции используется ключевое слово operator, вслед за которым помещен знак операции. Если принять, что конструкция operator знак_операции есть имя некоторой функции, то определение и прототип операции-функции подобны определению и прототипу обычной функции языка C++. Например, для распространения действия бинарной операции "*" на объекты класса T может быть введена функция с заголовком:

  Т operator * (Т x, Т y)

    Определенная таким образом операция (в нашем примере операция "звездочка") называется перегруженной (Overload), а сам механизм - перегрузкой или расширением действия стандартных операций.

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

    Начнем с последнего варианта.

    Если для класса T введена операция-функция с приведенным выше заголовком и определены два объекта A, B класса T, то выражение A * B интерпретируется как вызов функции operator * (A,B).

    В качестве содержательного примера распространим действие операции "+" на объекты класса "символьные строки". Для этого используется определенный на 6 шаге класс stroka, в котором len - длина строки и ch - указатель на символьный массив с текстом строки.

//STROKA.CPP - файл с определением класса   "символьная
//	строка"
#include <string.h>  // Для библиотечных строковых функций.
#include <iostream.h>
class stroka
{ // Скрытые от внешнего доступа данные:
     char *ch; // Указатель на текстовую строку.
     int len;  // Длина текстовой строки.
     public: // Общедоступные функции:
         // Конструкторы объектов класса:
         // Создает объект как новую пустую строку:
         stroka(int N = 80):
           // Строка не содержит информации:
              len(0)
             { ch = new char[N + 1]; // Память выделена для массива.
               ch[0] = '\0';
             }
         // Создает объект по заданной строке: 
         stroka (const char *arch)
         { len = strlen(arch); 
           ch = new char[len+1]; 
           strcpy(ch,arch);
         }
       int& len_str(void) // Возвращает ссылку на длину строки.
       { return len; }
       char *string(void) // Возвращает указатель на строку.
       { return ch; }
       void display(void) // Печатает информацию о строке.
       { cout << "\nДлина строки: " << len; 
         cout << "\nСодержимое строки: " << ch;
       }
      // Деструктор - освобождает память объекта: 
      ~stroka() { delete [] ch; }
};
Текст этого класса можно взять здесь.

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

   stroka& operator + (stroka& A, stroka& В),

распространяющую действие операции "+" на объекты класса stroka. Определение операции-функции размещено ниже основной программы, в которой используется выражение с операцией "+", примененной к объектам класса stroka. Указанное размещение текста определения операции-функции потребовало применения ее прототипа, который помещен до функции main(). Текст программы:

//OOP16_1.СРР  - расширение действия (перегрузка) операции "+".
// Определение  класса   "символьные  строки":
#include "stroka.cpp"
//Прототип функции для расширения действия операции  "+".
stroka&  operator + (stroka& A, stroka& B);
void main(void)
{   stroka X("Qui");
    stroka Y(" Vivra");
    stroka Z(" Verra!");
    stroka C;
    C=X+Y+Z+" - Поживем - увидим!";
    C.display();
}
//Расширение действия операции "+"
//на строковые операнды:
stroka& operator + (stroka& a, stroka& b)
{ // Длина строки-результата:
  int ii = a.len_str() + b.len_str();
  stroka *ps;     // Вспомогательный указатель.
  // Создаем объект в динамической памяти:
  ps = new stroka(ii);
  // Копируем строку из 'а':
  strcpy(ps->string(),a.string());
  // Присоединяем строку из 'b':
  strcat(ps->string(),b.string());
  ps->len_str() = ii; // Записываем значение длины строки.
  return *ps;         // Возвращаем новый объект stroka.
}
Текст этой программы можно взять здесь.

    Результат выполнения программы:

   Длина строки: 36
   Содержимое строки: Qui Vivra Verra! - Поживем - увидим!

    В программе операция-функция, расширяющая действие операции "+" на операнды типа stroka&, используется трижды в одном выражении:

   X + Y + Z +" -  Поживем - увидим!"

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

   X + Y + Z +" -  Поживем - увидим!"
   operator знак_операции (фактические_параметры);

    Например, к тому же результату в нашем примере приведет следующая последовательность операторов:

   C = operator + (X,Y); 
   C = operator + (C,Z); 
   C = operator + (C," - Поживем - увидим!");

    На следующем шаге мы продолжим знакомство с перегрузкой операций.




Предыдущий шаг Содержание Следующий шаг