Теперь любая константная функция способна модифицировать _cursor, и move() может быть объявлена константной. Хотя move() изменяет данный член, компилятор не считает это ошибкой.

// move() - константная функция-член

inline void Screen::move( int r, int c ) const

{

// ...

// правильно: константная функция-член может модифицировать члены

// со спецификатором mutable

_cursor = row + c - 1;

// ...

}

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

Отметим, что изменчивым объявлен только член _cursor, тогда как _screen, _height и _width не имеют спецификатора mutable, поскольку их значения в константном объекте класса Screen изменять нельзя.

Упражнение 13.3

Объясните, как будет вести себя copy() при следующих вызовах:

Screen myScreen;

myScreen.copy( myScreen );

Упражнение 13.4

К дополнительным перемещениям курсора можно отнести его передвижение вперед и назад на один символ. Из правого нижнего угла экрана курсор должен попасть в левый верхний угол. Реализуйте функции forward() и backward().

Упражнение 13.5

Еще одной полезной возможностью является перемещение курсора вниз и вверх на одну строку. По достижении верхней или нижней строки экрана курсор не перепрыгивает на противоположный край; вместо этого подается звуковой сигнал, и курсор остается на месте. Реализуйте функции up() и down(). Для подачи сигнала следует вывести на стандартный вывод cout символ с кодом '007'.

Упражнение 13.6

Пересмотрите описанные функции-члены класса Screen и объявите те, которые сочтете нужными, константными. Объясните свое решение.

<p>13.4. Неявный указатель this</p>

У каждого объекта класса есть собственная копия данных-членов. Например:

int main() {

Screen myScreen( 3, 3 ), bufScreen;

myScreen.clear();

myScreen.move( 2, 2 );

myScreen.set( '*' );

myScreen.display();

bufScreen.resize( 5, 5 );

bufScreen.display();

}

У объекта myScreen есть свои члены _width, _height, _cursor и _screen, а у объекта bufScreen – свои. Однако каждая функция-член класса существует в единственном экземпляре. Их и вызывают myScreen и bufScreen.

В предыдущем разделе мы видели, что функция-член может обращаться к членам своего класса, не используя операторы доступа. Так, определение функции move() выглядит следующим образом:

inline void Screen::move( int r, int c )

{

if ( checkRange( r, c ) ) // позиция на экране задана корректно?

{

int row = (r-1) * _width; // смещение строки

_cursor = row + c - 1;

}

}

Если функция move() вызывается для объекта myScreen, то члены _width и _height, к которым внутри нее имеются обращения, – это члены объекта myScreen. Если же она вызывается для объекта bufScreen, то и обращения производятся к членам данного объекта. Каким же образом _cursor, которым манипулирует move(), оказывается членом то myScreen, то bufScreen? Дело в указателе this.

Каждой функции-члену передается указатель на объект, для которого она вызвана, – this. В неконстантной функции-члене это указатель на тип класса, в константной – константный указатель на тот же тип, а в функции со спецификатором volatile указатель с тем же спецификатором. Например, внутри функции-члена move() класса Screen указатель this имеет тип Screen*, а в неконстантной функции-члене List – тип List*.

Поскольку this адресует объект, для которого вызвана функция-член, то при вызове move() для myScreen он указывает на объект myScreen, а при вызове для bufScreen – на объект bufScreen. Таким образом, член _cursor, с которым работает функция move(), в первом случае принадлежит объекту myScreen, а во втором – bufScreen.

Понять все это можно, если представить себе, как компилятор реализует объект this. Для его поддержки необходимо две трансформации:

* Изменить определение функции-члена класса, добавив дополнительный параметр:

// псевдокод, показывающий, как происходит расширение

// определения функции-члена

// ЭТО НЕ КОРРЕКТНЫЙ КОД C++

inline void Screen::move( Screen *this, int r, int c )

{

if ( checkRange( r, c ) )

{

int row = (r-1) * this-_width;

this-_cursor = row + c - 1;

}

}

В этом определении использование указателя this для доступа к членам _width и _cursor сделано явным.

Перейти на страницу:

Похожие книги