►Переопределение оператора присвоения...273

Следующая программа демонстрирует переопределение оператора присвоения. В программе также представлен конструктор копирования — просто для сравнения.

    //

    /* DemoAssignmentOperator — демонстрация оператора */

    /*          присвоения для пользовательского класса */

    //

    #include <cstdio>

    #include <cstdlib>

    #include <iostream>

    #include <strings.h>

    using namespace std ;

    /* Name — класс для демонстрации присвоения и конструктора копирования */

    class Name

    {

      public :

        Name( char *pszN = 0 )

        {

            copyName( pszN , " " ) ;

        }

        Name( Name& s )

        {

            cout << "Вызов конструктора копирования" << endl ;

            copyName( s.pszName , " ( copy ) " ) ;

        }

        ~Name( )

        {

            deleteName( ) ;

        }

        /* Оператор присвоения */

        Name& operator=( Name& s )

        {

            cout << "Выполнение присвоения" << endl ;

_________________

273 стр. Глава 23. Оператор присвоения

            /* Удаляем выделенную память... */

            deleteName( ) ;

            /* ...перед заменой её новым блоком */

            copyName( s.pszName , " ( replaced ) " ) ;

            /* Возвращаем ссылку  на существующий объект */

            return *this ;

        }

        /* Очень простая функция доступа */

        char* out( ) { return pszName ; }

      protected :

        void copyName( char* pszN , char* pszAdd ) ;

        void deleteName( ) ;

        char *pszName ;

    } ;

    /* copyName( ) — Выделение памяти из кучи и сохранение строк в ней */

    void Name::copyName( char* pszN , char* pszAdd )

    {

        pszName = 0 ;

        if ( pszN )

        {

            pszName = new char[ strlen( pszN ) +

                               strlen( pszAdd ) + 1 ] ;

            strcpy( pszName , pszN ) ;

            strcat( pszName , pszAdd ) ;

        }

    }

    /* deleteName( ) — возврат памяти в куче */

    void Name::deleteName( )

    {

        if ( pszName )

        {

            delete pszName ;

            pszName = 0 ;

        }

    }

    int main( int nNumberofArgs , char* pszArgs[ ] )

    {

      setlocale ( LC_ALL , ".1251" ) ; /* печать русских текстов */

        /* Создание двух объектов */

        Name n1( "Claudette" ) ;

        Name n2( "Greg" ) ;

        cout << "n1 ( " << n1.out( ) << " ) и "

             <<"n2 ( " << n2.out( ) << " ) — "

             << "вновь созданные объекты"

             << endl ;

        /* Конструктор копирования */

        cout << "Name n3( n1 ) ;" << endl ;

        Name n3( n1 ) ;

        cout << "n3 ( " << n3.out( ) << " ) — копия n1" << endl ;

_________________

274 стр. Часть 5. Полезные особенности

        /* Создание нового объекта с использованием формата с "=" для обращения к конструктору копирования */

        cout << "Name n4 = n1 ;" << endl ;

        Name n4 = n1 ;

        cout << "n4 ( " << n4.out( ) << " ) — ещё одна копия n1"

             << endl ;

        /* Перезапись n2 объектом n1 */

        cout << "n2 = n1" << endl ;

        n2 = n1 ;

        cout << "n1 ( " << n1.out( ) << " ) присвоен объекту "

             << "n2 ( " << n2.out( ) << " )" << endl ;

        /* Пауза для того, чтобы посмотреть на результат работы программы */

        system( "PAUSE" ) ; return 0 ;

    } 

  

Класс Name содержит указатель на имя человека, которое записывается в блок памяти, выделяемый из кучи. Конструктор и деструктор класса Name аналогичны представленным в главах 17, "Аргументация конструирования", и 18, "Копирующий конструктор". Конструктор Name( char* ) копирует переданное ему имя в член pszName. Этот конструктор служит также в роли конструктора по умолчанию. Конструктор копирования Name( Name& ) копирует имя переданного объекта при помощи функции-члена copyName( ). Деструктор освобождает блок памяти при помощи вызова deleteName( ).

Оператор присвоения operator=( ) является методом класса. Он выглядит как деструктор, за которым тут же следует конструктор копирования, что представляет собой вполне типичную ситуацию. Рассмотрим присвоение n2 = n1. Объект n2 уже имеет связанное с ним имя "Greg". В процессе присвоения память, выделенная для этого имени, освобождается при помощи вызова deleteName( ), так же, как это делается в деструкторе. Затем оператор присвоения вызывает copyName( ) для копирования новой информации в объект, подобно тому, как это делается в конструкторе копирования.

Конструктору копирования не нужно вызывать deleteName( ), поскольку объект в этот момент ещё не существует, и память из кучи не выделена. Соответственно, деструктору не надо вызывать функцию копирования.

Есть ещё пара деталей, о которых следует упомянуть. Во-первых, возвращаемый оператором присвоения тип — Name&. Выражения, включающие оператор присвоения, имеют тип и значение, которые совпадают с типом и значением левого аргумента после присвоения. В следующем примере значение operator=( ) равно 2.0 , а его тип — double.

    double d1 , d2 ;

    void fn( double )

    d1 = 2.0 ; /* Значение этого выражения равно 2.0 */

Это позволяет программисту написать следующее:

    d2 = d1 = 2.0 ;

    fn( d2 = 3.0 ) ; /* Выполняет присвоение и передаёт полученное значение функции fn( ) */

Значение присвоения d1 = 2.0, равное 2.0, и его тип double передаются для присвоения d2. Во втором примере значение присвоения d2 = 3.0 передаётся функции fn( ).

Во-вторых, оператор присвоения является функцией-членом. Её левый аргумент — текущий объект ( this ). В отличие от других операторов, оператор присвоения не может быть перегружен при помощи функции — не члена класса.

_________________

275 стр. Глава 23. Оператор присвоения