С этого шага мы начнем знакомиться с механизмом перегрузки стандартных операций.
Одной из особенностей языка 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," - Поживем - увидим!");
На следующем шаге мы продолжим знакомство с перегрузкой операций.