Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Языки программирования / Литература по C++ / С.Липпман.С++ для начинающих.pdf
Скачиваний:
143
Добавлен:
31.05.2015
Размер:
5.55 Mб
Скачать

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

734

#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;

}

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

15.7. Операторы инкремента и декремента

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

Сначала мы определим новый член size, который содержит либо нуль (это говорит о том, что объект ScreenPtr указывает на единственный объект), либо размер массива, адресуемого объектом ScreenPtr. Нам также понадобится член offset, запоминающий смещение от начала данного массива:

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

735

class ScreenPtr {

 

public:

 

// ...

 

private:

// размер массива: 0, если единственный объект

int size;

int offset;

// смещение ptr от начала массива

Screen *ptr;

 

};

 

Модифицируем конструктор класса ScreenPtr с учетом его новой функциональности и дополнительных членов,. Пользователь нашего класса должен передать конструктору

class ScreenPtr { public:

ScreenPtr( Screen &s , int arraySize = 0 )

: ptr( &s ), size ( arraySize ), offset( 0 ) { }

private: int size;

int offset; Screen *ptr;

дополнительный аргумент, если создаваемый объект указывает на массив:

};

С помощью этого аргумента задается размер массива. Чтобы сохранить прежнюю функциональность, предусмотрим для него значение по умолчанию, равное нулю. Таким образом, если второй аргумент конструктора опущен, то член size окажется равен 0 и, следовательно, такой объект будет указывать на единственный объект Screen. Объекты

Screen myScreen( 4, 4 );

ScreenPtr pobj( myScreen ); // правильно: указывает на один объект const int arrSize = 10;

Screen *parray = new Screen[ arrSize ];

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

ScreenPtr parr( *parray, arrSize ); // правильно: указывает на массив

Теперь мы готовы определить в ScreenPtr перегруженные операторы инкремента и декремента. Однако они бывают двух видов: префиксные и постфиксные. К счастью, можно определить оба варианта. Для префиксного оператора объявление не содержит

class ScreenPtr { public:

Screen& operator++(); Screen& operator--(); // ...

ничего неожиданного:

};

Такие операторы определяются как унарные операторные функции. Использовать префиксный оператор инкремента можно, к примеру, следующим образом:

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

736

const int arrSize = 10;

Screen *parray = new Screen[ arrSize ]; ScreenPtr parr( *parray, arrSize );

for ( int ix = 0;

ix < arrSize;

++ix, ++parr ) // эквивалентно parr.operator++()

}

printScreen( parr );

Screen& ScreenPtr::operator++()

{

if ( size == 0 ) {

cerr << "не могу инкрементировать указатель для одного объекта\n"; return *ptr;

}

if ( offset >= size - 1 ) {

cerr << "уже в конце массива\n"; return *ptr;

}

++offset; return *++ptr;

}

Screen& ScreenPtr::operator--()

{

if ( size == 0 ) {

cerr << "не могу декрементировать указатель для одного объекта\n"; return *ptr;

}

if ( offset <= 0 ) {

cerr << "уже в начале массива\n"; return *ptr;

}

--offset; return *--ptr;

Определения этих перегруженных операторов приведены ниже:

}

Чтобы отличить префиксные операторы от постфиксных, в объявлениях последних имеется дополнительный параметр типа int. В следующем фрагменте объявлены префиксные и постфиксные варианты операторов инкремента и декремента для класса

class ScreenPtr {

 

public:

// префиксные операторы

Screen& operator++();

Screen& operator--();

 

Screen& operator++(int); // постфиксные операторы Screen& operator--(int);

// ...

ScreenPtr:

};

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

737

Screen& ScreenPtr::operator++(int)

{

if ( size == 0 ) {

cerr << "не могу инкрементировать указатель для одного объекта\n"; return *ptr;

}

if ( offset == size ) {

cerr << "уже на один элемент дальше конца массива\n"; return *ptr;

}

++offset; return *ptr++;

}

Screen& ScreenPtr::operator--(int)

{

if ( size == 0 ) {

cerr << "не могу декрементировать указатель для одного объекта\n"; return *ptr;

}

if ( offset == -1 ) {

cerr << "уже на один элемент раньше начала массива\n"; return *ptr;

}

--offset; return *ptr--;

Ниже приведена возможная реализация постфиксных операторов:

}

Обратите внимание, что давать название второму параметру нет необходимости, поскольку внутри определения оператора он не употребляется. Компилятор сам подставляет для него значение по умолчанию, которое можно игнорировать. Вот пример

const int arrSize = 10;

Screen *parray = new Screen[ arrSize ]; ScreenPtr parr( *parray, arrSize );

for ( int ix = 0; ix < arrSize; ++ix)

использования постфиксного оператора:

printScreen( parr++ );

При его явном вызове необходимо все же передать значение второго целого аргумента. В случае нашего класса ScreenPtr это значение игнорируется, поэтому может быть любым:

parr.operator++(1024); // вызов постфиксного operator++

Перегруженные операторы инкремента и декремента разрешается объявлять как дружественные функции. Изменим соответствующим образом определение класса

ScreenPtr: