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