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

       

Оператор “стрелка”


Оператор “стрелка”, разрешающий доступ к членам, может перегружаться для объектов класса. Он должен быть определен как функция-член и обеспечивать семантику указателя. Чаще всего этот оператор используется в классах, которые предоставляют “интеллектуальный указатель” (smart pointer), ведущий себя аналогично встроенным, но поддерживают и некоторую дополнительную функциональность.

Допустим, мы хотим определить тип класса для представления указателя на объект Screen (см. главу 13):

class ScreenPtr {

   // ...

private:

   Screen *ptr;

};

Определение ScreenPtr должно быть таким, чтобы объект этого класса гарантировано указывал на объект Screen: в отличие от встроенного указателя, он не может быть нулевым. Тогда приложение сможет пользоваться объектами типа ScreenPtr, не проверяя, указывают ли они на какой-нибудь объект Screen. Для этого нужно определить класс ScreenPtr с конструктором, но без конструктора по умолчанию (детально конструкторы рассматривались в разделе 14.2):

class ScreenPtr {

public:

   ScreenPtr( const Screen &s ) : ptr( &s ) { }

   // ...

};



В любом определении объекта класса ScreenPtr должен присутствовать инициализатор– объект класса Screen, на который будет ссылаться объект ScreenPtr:

ScreenPtr p1;   // ошибка: у класса ScreenPtr нет конструктора по умолчанию

Screen myScreen( 4, 4 );

ScreenPtr ps( myScreen );  // правильно

Чтобы класс ScreenPtr вел себя как встроенный указатель, необходимо определить некоторые перегруженные операторы – разыменования (*) и “стрелку” для доступа к членам:

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

class ScreenPtr {

public:

   Screen& operator*()  { return *ptr; }

   Screen* operator->() { return ptr; }

   // ...

};

Оператор доступа к членам унарный, поэтому параметры ему не передаются. При использовании в составе выражения его результат зависит только от типа левого операнда. Например, в инструкции

point->action();

исследуется тип point. Если это указатель на некоторый тип класса, то применяется семантика встроенного оператора доступа к члену. Если же это объект или ссылка на объект, то проверяется, есть ли в этом классе перегруженный оператор доступа. Когда перегруженный оператор “стрелка” определен, он вызывается для объекта point, иначе инструкция неверна, поскольку для обращения к членам самого объекта (в том числе по ссылке) следует использовать оператор “точка”.


Перегруженный оператор “стрелка” должен возвращать либо указатель на тип класса, либо объект класса, в котором он определен. Если возвращается указатель, то к нему применяется семантика встроенного оператора “стрелка”. В противном случае процесс продолжается рекурсивно, пока не будет получен указатель или определена ошибка. Например, так можно воспользоваться объектом ps класса ScreenPtr для доступа к членам Screen:

ps->move( 2, 3 );

Поскольку слева от оператора “стрелка” находится объект типа ScreenPtr, то употребляется перегруженный оператор этого класса, который возвращает указатель на объект Screen. Затем к полученному значению применяется встроенный оператор “стрелка” для вызова функции-члена move().

Ниже приводится небольшая программа для тестирования класса ScreenPtr. Объект типа ScreenPtr используется точно так же, как любой объект типа Screen*:

#include <iostream>

#include <string>

#include "Screen.h"

void printScreen( const ScreenPtr &ps )

{

   cout << "Screen Object ( "

        << ps->height() << ", "

        << ps->width() << " )\n\n";

   for ( int ix = 1; ix <= ps->height(); ++ix )

   {

      for ( int iy = 1; iy <= ps->width(); ++iy )

         cout << ps->get( ix, iy );

      cout << "\n";

    }

}

int main() {

   Screen sobj( 2, 5 );

   string init( "HelloWorld" );

   ScreenPtr ps( sobj );

   // Установить содержимое экрана

   string::size_type initpos = 0;

   for ( int ix = 1; ix <= ps->height(); ++ix )

      for ( int iy = 1; iy <= ps->width(); ++iy )

      {

         ps->move( ix, iy );

         ps->set( init[ initpos++ ] );

      }

    // Вывести содержимое экрана

    printScreen( ps );

    return 0;

}

Разумеется, подобные манипуляции с указателями на объекты классов не так эффективны, как работа со встроенными указателями. Поэтому интеллектуальный указатель должен предоставлять дополнительную функциональность, важную для приложения, чтобы оправдать сложность своего использования.


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







Forekc.ru
Рефераты, дипломы, курсовые, выпускные и квалификационные работы, диссертации, учебники, учебные пособия, лекции, методические пособия и рекомендации, программы и курсы обучения, публикации из профильных изданий