Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих.pdf
Скачиваний:
183
Добавлен:
01.05.2014
Размер:
3.97 Mб
Скачать

class Screen { public:

// объявления перегруженных функций-членов get()

char get() { return _screen[_cursor]; } char get( int, int );

// ...

};

(Подробнее мы остановимся на функциях-членах класса в разделе 13.3.)

13.1.3. Доступ к членам

Часто бывает так, что внутреннее представление типа класса изменяется в последующих версиях программы. Допустим, опрос пользователей нашего класса Screen показал, что

для его объектов всегда задается размер экрана 80 × 24. В таком случае было бы желательно заменить внутреннее представление экрана менее гибким, но более

class Screen { public:

//функции-члены private:

//инициализация статических членов (см.

13.5)

_height = 24;

static const int

static const int

_width = 80;

string

_screen;

string::size_type

_cursor;

эффективным:

};

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

Если бы данные-члены класса Screen были открыты и доступны любой функции внутри программы, как отразилось бы на пользователях изменение внутреннего представления этого класса?

все функции, которые напрямую обращались к данным-членам старого представления, перестали бы работать. Следовательно, пришлось бы отыскивать

иизменять соответствующие части кода;

так как интерфейс не изменился, то коды, манипулировавшие объектами класса Screen только через функции-члены, не пришлось бы модифицировать. Но поскольку сами функции-члены все же изменились, программу пришлось бы откомпилировать заново.

Сокрытие информации – это формальный механизм, предотвращающий прямой доступ к внутреннему представлению типа класса из функций программы. Ограничение доступа к членам задается с помощью секций тела класса, помеченных ключевыми словами public, private и protected спецификаторами доступа. Члены, объявленные в

секции public, называются открытыми, а объявленные в секциях private и protected соответственно закрытыми или защищенными.

открытый член доступен из любого места программы. Класс, скрывающий информацию, оставляет открытыми только функции-члены, определяющие операции, с помощью которых внешняя программа может манипулировать его объектами;

закрытый член доступен только функциям-членам и друзьям класса. Класс, который хочет скрыть информацию, объявляет свои данные-члены закрытыми;

защищенный член ведет себя как открытый по отношению к производному классу и как закрытый по отношению к остальной части программы. (В главе 2 мы видели пример использования защищенных членов в классе IntArray. Детально они рассматриваются в главе 17, где вводится понятие наследования.)

class Screen {

public:

void home() { _cursor = 0; } char get() { return

_screen[_cursor]; } char get( int, int );

void move( int, int );

// ...

 

private:

_screen;

string

string::size_type _cursor;

short

_height, _width;

В следующем определении класса Screen указаны секции public и private:

};

Согласно принятому соглашению, сначала объявляются открытые члены класса. (Обсуждение того, почему в старых программах C++ сначала шли закрытые члены и почему этот стиль еще кое-где сохранился, см. в книге [LIPPMAN96a].) В теле класса может быть несколько секций public, protected и private. Каждая секция продолжается либо до метки следующей секции, либо до закрывающей фигурной скобки. Если спецификатор доступа не указан, то секция, непосредственно следующая за открывающей скобкой, по умолчанию считается private.

13.1.4. Друзья

Иногда удобно разрешить некоторым функциям доступ к закрытым членам класса. Механизм друзей позволяет классу разрешать доступ к своим неоткрытым членам.

Объявление друга начинается с ключевого слова friend и может встречаться только внутри определения класса. Так как друзья не являются членами класса, то не имеет значения, в какой секции они объявлены. В примере ниже мы сгруппировали все подобные объявления сразу после заголовка класса:

class Screen { friend istream&

operator>>( istream&, Screen& ); friend ostream&

operator<<( ostream&, const Screen& );

public:

// ... оставшаяся часть класса Screen

};

Операторы ввода и вывода теперь могут напрямую обращаться к закрытым членам

#include <iostream>

ostream& operator<<( ostream& os, const Screen& s )

{

// правильно: можно обращаться к _height, _width и _screen

os << "<" << s._height

<< "," << s._width << ">"; os << s._screen;

return os;

класса Screen. Простая реализация оператора вывода выглядит следующим образом:

}

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

13.1.5. Объявление и определение класса

О классе говорят, что он определен, как только встретилась скобка, закрывающая его тело. После этого становятся известными все члены класса, а следовательно, и его размер.

Можно объявить класс, не определяя его. Например:

class Screen; // объявление класса Screen

Это объявление вводит в программу имя Screen и указывает, что оно относится к типу класса.

Тип объявленного, но еще не определенного класса допустимо использовать весьма ограниченно. Нельзя определять объект типа класса, если сам класс еще не определен, поскольку размер класса в этом момент неизвестен и компилятор не знает, сколько памяти отвести под объект.

Однако указатель или ссылку на объект такого класса объявлять можно, так как они имеют фиксированный размер, не зависящий от типа. Но, поскольку размеры класса и его членов неизвестны, применять оператор разыменования (*) к такому указателю, а также использовать указатель или ссылку для обращения к члену не разрешается, пока класс не будет полностью определен.

Член некоторого класса можно объявить принадлежащим к типу какого-либо класса только тогда, когда компилятор уже видел определение этого класса. До этого объявляются лишь члены, являющиеся указателями или ссылками на такой тип. Ниже приведено определение StackScreen, один из членов которого служит указателем на

class

Screen; // объявление

class

StackScreen {

int topStack;

// правильно: указатель на объект Screen

Screen *stack; void (*handler)();

Screen, который объявлен, но еще не определен:

};

Поскольку класс не считается определенным, пока не закончилось его тело, то в нем не может быть данных-членов его собственного типа. Однако класс считается объявленным, как только распознан его заголовок, поэтому в нем допустимы члены, являющиеся

class LinkScreen { Screen window; LinkScreen

*next; LinkScreen

*prev;

ссылками или указателями на его тип. Например:

};

Упражнение 13.1

string

_

n a m e

;

Пусть дан класс Person со следующими двумя членами: string _address;

Person( const string &n, const string &s )

: _name( n ), _address( a ) { } string name() { return _name; }

и такие функции-члены:

string address() { return _address; }