С++ для начинающих


Перегрузка операторов


В предыдущих главах мы уже показывали, что перегрузка операторов позволяет программисту вводить собственные версии предопределенных операторов (см. главу 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.)

Во втором наборе есть всего один оператор – взятия индекса:


// перегруженный оператор взятия индекса

char& operator[]( int );

Он позволяет программе индексировать объекты класса String точно так же, как массивы объектов встроенного типа:

if ( name[0] != 'S' )

   cout << "увы, что-то не так\n";

(Детально этот оператор описывается в разделе 15.4.)

В третьем наборе определены перегруженные операторы равенства для объектов класса String. Программа может проверить равенство двух таких объектов или объекта и C-строки:

// набор перегруженных операторов равенства

// str1 == str2;

bool operator==( const char * );

bool operator==( const String & );

Перегруженные операторы позволяют использовать объекты типа класса с операторами, определенными в главе 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;

}


Содержание раздела