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

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

   
Одной из особенностей языка 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," - Поживем - увидим!");

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



Вы можете оставить комментарий, или Трекбэк с вашего сайта.

Оставить комментарий