Язык программирования C++ |
Дополнительные возможности классов
Переопределение операций
Язык Си++ позволяет определять в классах особого вида методы – операции. Они называются операциями потому, что их запись имеет тот же вид, что и запись операции сложения, умножения и т.п. со встроенными типами языка Си++.
Определим две операции в классе String – сравнение на меньше и сложение:
class String { public: . . . String operator+(const String& s) const; bool operator<(const String& s) const; };
Признаком того, что переопределяется операция, служит ключевое слово operator, после которого стоит знак операции . В остальном операция мало чем отличается от обычного метода класса. Теперь в программе можно записать:
String s1, s2; . . . s1 + s2
Объект s1 выполнит метод operator с объектом s2 в качестве аргумента.
Результатом операции сложения является объект типа String. Никакой из аргументов операции не изменяется. Описатель const при описании аргумента говорит о том, что s2 не может измениться при выполнении сложения, а описатель const в конце определения операции говорит то же самое об объекте, выполняющем сложение.
Реализация может выглядеть следующим образом:
String String::operator+(const String& s) const { String result; result.length = length + s.length; result.str = new char[result.length + 1]; strcpy(result.str, str); strcat(result.str, s.str); return result; }
При сравнении на меньше мы будем сравнивать строки в лексикографической последовательности. Проще говоря, меньше та строка, которая должна стоять раньше по алфавиту:
bool String::operator<(const String& s) const { char* cp1 = str; char* cp2 = s.str; while (true) { if (*cp1 < *cp2) return true; else if (*cp1 > *cp2) return false; else { cp1++; cp2++; if (*cp2 == 0) // конец строки return false; else if (*cp1 == 0) // конец строки return true; } } }
Как определять операции
Если для класса определяют операции, то обычно определяют достаточно полный их набор, так, чтобы объекты этого класса могли участвовать в полноценных выражениях.
Прежде всего, определим операцию присваивания. Операция присваивания в качестве аргумента использует объект того же класса и копирует значение этого объекта. Однако, в отличие от копирующего конструктора, у объекта уже имеется какое-то свое значение, и его нужно аккуратно уничтожить.
class String { public: // объявление операции присваивания String& operator=(const String& s); }; // Реализация присваивания String& String::operator=(const String& s) { if (this == &s) return *this; if (str != 0) { delete [] str; } length = s.length; str = new char[length + 1]; if (str == 0) { // обработка ошибок } strcpy(str, s.str); return *this; }
Обратим внимание на несколько важных особенностей операции присваивания. Во-первых, в качестве результата операции присваивания объект возвращает ссылку на самого себя. Это дает возможность использовать строки в выражениях типа:
s1 = s2 = s3;
Во-вторых, в начале операции проверяется, не равен ли аргумент самому объекту. Таким образом, присваивание s1 = s1 выполняется правильно и быстро.
В-третьих, перед тем как скопировать новое значение, операция присваивания освобождает память, занимаемую старым значением.
Аналогично операции присваивания можно определить операцию +=.
Набор операций, позволяющий задействовать класс String в различных выражениях, представлен ниже:
class String { public: String(); String(const String& s); String(const char*); String& operator=(const String& s); String& operator+=(const String& s); bool operator==(const String& s) const; bool operator!=(const String& s) const; bool operator<(const String& s) const; bool operator>(const String& s) const; bool operator<=(const String& s) const; bool operator>=(const String& s) const; String operator+(const String& s) const; };