logo
ВОПРОСЫ

13.4.1. Когда использовать указатель this

Наша функция main() вызывает функции-члены класса Screen для объектов myScreen и bufScreen таким образом, что каждое действие – это отдельная инструкция. У нас есть возможность определить функции-члены так, чтобы конкатенировать их вызовы при обращении к одному и тому же объекту. Например, все вызовы внутри main() будут выглядеть так:

int main() {

// ...

myScreen.clear().move( 2, 2 ), set( '*' ). display();

bufScreen.reSize( 5, 5 ).display();

}

Именно так интуитивно представляется последовательность операций с экраном: очистить экран myScreen, переместить курсор в позицию (2,2), записать в эту позицию символ '*' и вывести результат.

Операторы доступа "точка" и "стрелка" левоассоциативны, т.е. их последовательность выполняется слева направо. Например, сначала вызывается myScreen.clear(), затем myScreen.move() и т.д. Чтобы myScreen.move() можно было вызвать после myScreen.clear(), функция clear() должна возвращать объект myScreen, для которого она была вызвана. Мы уже видели, что доступ к объекту внутри функции-члена класса производится в помощью указателя this. Вот реализация clear():

// объявление clear() находится в теле класса

// в нем задан аргумент по умолчанию bkground = '#'

Screen& Screen::clear( char bkground )

{ // установить курсор в левый верхний угол и очистить экран

_cursor = 0;

_screen.assign( // записать в строку

_screen.size(), // size() символов

bkground // со значением bkground

);

// вернуть объект, для которого была вызвана функция

return *this;

}

Обратите внимание, что возвращаемый тип этой функции-члена – Screen& – ссылка на объект ее же класса. Чтобы конкатенировать вызовы, необходимо также пересмотреть реализацию move() и set(). Возвращаемый тип следует изменить с void на Screen&, а в определении возвращать *this.

Аналогично функцию-член display() можно написать так:

Screen& Screen::display()

{

typedef string::size_type idx_type;

for ( idx_type ix = 0; ix < _height; ++ix )

{ // для каждой строки

idx_type offset = _width * ix; // смещение строки

for ( idx_type iy = 0; iy < _width; ++iy )

// для каждой колонки вывести элемент

cout << _screen[ offset + iy ];

cout << endl;

}

return *this;

}

А вот реализация reSize():

// объявление reSize() находится в теле класса

// в нем задан аргумент по умолчанию bkground = '#'

Screen& Screen::reSize( int h, int w, char bkground )

{ // сделать высоту экрана равной h, а ширину - равной w

// запомнить содержимое экрана

string local(_screen);

// заменить строку _screen

_screen.assign( // записать в строку

h * w, // h * w символов

bkground // со значением bkground

);

typedef string::size_type idx_type;

idx_type local_pos = 0;

// скопировать содержимое старого экрана в новый

for ( idx_type ix = 0; ix < _height; ++ix )

{ // для каждой строки

idx_type offset = w * ix; // смещение строки

for ( idx_type iy = 0; iy < _width; ++iy )

// для каждой колонки присвоить новое значение

_screen[ offset + iy ] = local[ local_pos++ ];

}

_height = h;

_width = w;

// _cursor не меняется

return *this;

}

Работа указателя this не исчерпывается возвратом объекта, к которому была применена функция-член. При рассмотрении copy() в разделе 13.3 мы видели и другой способ его использования:

void Screen::copy( const Screen& sobj )

{

// если этот объект Screen и sobj - одно и то же,

// копирование излишне

if ( this != sobj )

{

// скопировать значение sobj в this

}

}

Указатель this хранит адрес объекта, для которого была вызвана функция-член. Если адрес, на который ссылается sobj, совпадает со значением this, то sobj и this относятся к одному и тому же объекту, так что операция копирования не нужна. (Мы еще встретимся с этой конструкцией, когда будем рассматривать копирующий оператор присваивания в разделе 14.7.)

Упражнение 13.7

Указатель this можно использовать для модификации адресуемого объекта, а также для его замены другим объектом того же типа. Например, функция-член assign() класса classType выглядит так. Можете ли вы объяснить, что она делает?

classType& classType::assign( const classType &source )

{

if ( this != &source )

{

this->~classType();

new (this) classType( source );

}

return *this;

}

Напомним, что ~classType – это имя деструктора. Оператор new выглядит несколько причудливо, но мы уже встречались с подобным в разделе 8.4.