Перегрузка операторов
В предыдущих главах мы уже показывали, что перегрузка операторов позволяет программисту вводить собственные версии предопределенных операторов (см. главу 4) для операндов типа классов. Например, в классе String из раздела 3.15 задано много перегруженных операторов. Ниже приведено его определение:
#include <iostream> class String; istream& operator>>( istream &, const String & ); ostream& operator<<( ostream &, const String & ); class String { public: // набор перегруженных конструкторов // для автоматической инициализации String( const char* = 0 ); String( const String & ); // деструктор: автоматическое уничтожение ~String(); // набор перегруженных операторов присваивания String& operator=( const String & ); String& operator=( const char * ); // перегруженный оператор взятия индекса char& operator[]( int ); // набор перегруженных операторов равенства // str1 == str2; bool operator==( const char * ); bool operator==( const String & ); // функции доступа к членам int size() { return _size; }; char * c_str() { return _string; } private: int _size; char *_string; |
};
В классе String
есть три набора перегруженных операторов. Первый – это набор операторов присваивания:
// набор перегруженных операторов присваивания String& operator=( const String & ); |
String& operator=( const char * );
Сначала идет копирующий оператор присваивания. (Подробно они обсуждались в разделе 14.7.) Следующий оператор поддерживает присваивание C-строки символов объекту типа String:
String name; |
name = "Sherlock"; // использование оператора operator=( char * )
(Операторы присваивания, отличные от копирующих, мы рассмотрим в разделе 15.3.)
Во втором наборе есть всего один оператор – взятия индекса:
// перегруженный оператор взятия индекса |
Он позволяет программе индексировать объекты класса String точно так же, как массивы объектов встроенного типа:
if ( name[0] != 'S' ) |
(Детально этот оператор описывается в разделе 15.4.)
В третьем наборе определены перегруженные операторы равенства для объектов класса String. Программа может проверить равенство двух таких объектов или объекта и C-строки:
// набор перегруженных операторов равенства // str1 == str2; bool operator==( const char * ); |
Перегруженные операторы позволяют использовать объекты типа класса с операторами, определенными в главе 4, и манипулировать ими так же интуитивно, как объектами встроенных типов. Например, желая определить операцию конкатенации двух объектов класса String, мы могли бы реализовать ее в виде функции-члена concat(). Но почему concat(), а не, скажем, append()? Выбранное нами имя логично и легко запоминается, но пользователь все же может забыть, как мы назвали функцию. Зачастую имя проще запомнить, если определить перегруженный оператор. К примеру, вместо concat() мы назвали бы новую операцию operator+=(). Такой оператор используется следующим образом:
#include "String.h" int main() { String name1 "Sherlock"; String name2 "Holmes"; name1 += " "; name1 += name2; if (! ( name1 == "Sherlock Holmes" ) ) cout << "конкатенация не сработала\n"; |
Перегруженный оператор объявляется в теле класса точно так же, как обычная функция-член, только его имя состоит из ключевого слова operator, за которым следует один из множества предопределенных в языке C++ операторов (см. табл. 15.1). Так можно объявить operator+=() в классе String:
class String { public: // набор перегруженных операторов += String& operator+=( const String & ); String& operator+=( const char * ); // ... private: // ... |
};
и определить его следующим образом:
#include <cstring> inline String& String::operator+=( const String &rhs ) { // Если строка, на которую ссылается rhs, непуста if ( rhs._string ) { String tmp( *this ); // выделить область памяти, достаточную // для хранения конкатенированных строк _size += rhs._size; delete [] _string; _string = new char[ _size + 1 ]; // сначала скопировать в выделенную область исходную строку // затем дописать в конец строку, на которую ссылается rhs strcpy( _string, tmp._string ); strcpy( _string + tmp._size, rhs._string ); } return *this; } inline String& String::operator+=( const char *s ) { // Если указатель s ненулевой if ( s ) { String tmp( *this ); // выделить область памяти, достаточную // для хранения конкатенированных строк _size += strlen( s ); delete [] _string; _string = new char[ _size + 1 ]; // сначала скопировать в выделенную область исходную строку // затем дописать в конец C-строку, на которую ссылается s strcpy( _string, tmp._string ); strcpy( _string + tmp._size, s ); } return *this; |