С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 102
Текст из файла (страница 102)
Класс, скрывающийинформацию, оставляет открытыми только функции-члены, определяющие операции,с помощью которых внешняя программа может манипулировать его объектами;•закрытый член доступен только функциям-членам и друзьям класса. Класс, которыйхочет скрыть информацию, объявляет свои данные-члены закрытыми;•защищенный член ведет себя как открытый по отношению к производному классу икак закрытый по отношению к остальной части программы. (В главе 2 мы виделипример использования защищенных членов в классе IntArray.
Детально онирассматриваются в главе 17, где вводится понятие наследования.)class Screen {public:void home() { _cursor = 0; }char get() { return _screen[_cursor]; }char get( int, int );void move( int, int );// ...private:string_screen;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подобные объявления сразу после заголовка класса:};589С++ для начинающихОператоры ввода и вывода теперь могут напрямую обращаться к закрытым членам#include <iostream>ostream& operator<<( ostream& os, const Screen& s ){// правильно: можно обращаться к _height, _width и _screenos << "<" << s._height<< "," << s._width << ">";os << s._screen;return os;класса Screen.
Простая реализация оператора вывода выглядит следующим образом:}Другом может быть функция из пространства имен, функция-член другого класса илидаже целый класс. В последнем случае всем его функциям-членам предоставляетсядоступ к неоткрытым членам класса, объявляющего дружественные отношения. (Вразделе 15.2 друзья обсуждаются более подробно.)13.1.5. Объявление и определение классаО классе говорят, что он определен, как только встретилась скобка, закрывающая еготело.
После этого становятся известными все члены класса, а следовательно, и его размер.Можно объявить класс, не определяя его. Например:class Screen;// объявление класса ScreenЭто объявление вводит в программу имя Screen и указывает, что оно относится к типукласса.Тип объявленного, но еще не определенного класса допустимо использовать весьмаограниченно. Нельзя определять объект типа класса, если сам класс еще не определен,поскольку размер класса в этом момент неизвестен и компилятор не знает, сколькопамяти отвести под объект.Однако указатель или ссылку на объект такого класса объявлять можно, так как ониимеют фиксированный размер, не зависящий от типа. Но, поскольку размеры класса иего членов неизвестны, применять оператор разыменования (*) к такому указателю, атакже использовать указатель или ссылку для обращения к члену не разрешается, покакласс не будет полностью определен.Член некоторого класса можно объявить принадлежащим к типу какого-либо классатолько тогда, когда компилятор уже видел определение этого класса.
До этогообъявляются лишь члены, являющиеся указателями или ссылками на такой тип. Нижеприведено определение StackScreen, один из членов которого служит указателем наScreen, который объявлен, но еще не определен:590С++ для начинающихclass Screen;// объявлениеclass StackScreen {int topStack;// правильно: указатель на объект ScreenScreen *stack;void (*handler)();};Поскольку класс не считается определенным, пока не закончилось его тело, то в нем неможет быть данных-членов его собственного типа.
Однако класс считается объявленным,как только распознан его заголовок, поэтому в нем допустимы члены, являющиесяclass LinkScreen {Screen window;LinkScreen *next;LinkScreen *prev;ссылками или указателями на его тип. Например:};Упражнение 13.1string _name;Пусть дан класс Person со следующими двумя членами:string _address;Person( const string &n, const string &s ): _name( n ), _address( a ) { }string name() { return _name; }и такие функции-члены:string address() { return _address; }Какие члены вы объявили бы в секции public, а какие – в секции private? Пояснитесвой выбор.Упражнение 13.2Объясните разницу между объявлением и определением класса.
Когда вы стали быиспользовать объявление класса? А определение?13.2. Объекты классовОпределение класса, например Screen, не приводит к выделению памяти. Памятьвыделяется только тогда, когда определяется объект типа класса. Так, если имеетсяследующая реализация Screen:591С++ для начинающихclass Screen {public:// функции-членыprivate:stringstring:size_typeshortshort592_screen;_cursor;_height;_width;};то определениеScreen myScreen;выделяет область памяти, достаточную для хранения четырех членов Screen. ИмяmyScreen относится к этой области. У каждого объекта класса есть собственная копияданных-членов.
Изменение членов myScreen не отражается на значениях членов любогодругого объекта типа Screen.Область видимости объекта класса зависит от его положения в тексте программы. Онclass Screen {// список членов};int main(){Screen mainScreen;определяется в иной области, нежели сам тип класса:}Тип Screen объявлен в глобальной области видимости, тогда как объект mainScreen – влокальной области функции main().Объект класса также имеет время жизни.
В зависимости от того, где (в областивидимости пространства имен или в локальной области) и как (статическим илинестатическим) он объявлен, он может существовать в течение всего времени выполненияпрограммы или только во время вызова некоторой функции. Область видимости объектакласса и его время жизни ведут себя очень похоже.
(Понятия области видимости ивремени жизни введены в главе 8.)Объекты одного и того же класса можно инициализировать и присваивать друг другу. Поумолчанию копирование объекта класса эквивалентно копированию всех его членов.Screen bufScreen = myScreen;// bufScreen._height = myScreen._height;// bufScreen._width = myScreen._width;// bufScreen._cursor = myScreen._cursor;Например:// bufScreen._screen = myScreen._screen;С++ для начинающих593Указатели и ссылки на объекты класса также можно объявлять. Указатель на тип классаразрешается инициализировать адресом объекта того же класса или присвоить ему такойадрес.
Аналогично ссылка инициализируется l-значением объекта того же класса. (Вобъектно-ориентированном программировании указатель или ссылка на объект базовогоint main(){Screen myScreen, bufScreen[10];Screen *ptr = new Screen;myScreen = *ptr;delete ptr;ptr = bufScreen;Screen &ref = *ptr;Screen &ref2 = bufScreen[6];класса могут относиться и к объекту производного от него класса.)}По умолчанию объект класса передается по значению, если он выступает в ролиаргумента функции или ее возвращаемого значения. Можно объявить формальныйпараметр функции или возвращаемое ею значение как указатель или ссылку на типкласса. (В разделе 7.3 были представлены параметры, являющиеся указателями илиссылками на типы классов, и объяснялось, когда их следует использовать.
В разделе 7.4 сэтой точки зрения рассматривались типы возвращаемых значений.)Для доступа к данным или функциям-членам объекта класса следует пользоватьсясоответствующими операторами. Оператор “точка” (.) применяется, когда операндомявляется сам объект или ссылка на него; а “стрелка” (->) – когда операндом служит#include "Screen.h"bool isEqual( Screen& s1, Screen *s2 ){ // возвращает false, если объекты не равны, и true - если равныif (s1.height() != s2->height() ||s2.width() != s2->width() )return false;for ( int ix = 0; ix < s1.height(); ++ix )for ( int jy = 0; jy < s2->width(); ++jy )if ( s1.get( ix, jy ) != s2->get( ix, jy ) )return false;return true;// попали сюда? значит, объекты равныуказатель на объект:}isEqual() – это не являющаяся членом функция, которая сравнивает два объектаScreen. У нее нет права доступа к закрытым членам Screen, поэтому напрямуюобращаться к ним она не может.
Сравнение проводится с помощью открытых функцийчленов данного класса.Для получения высоты и ширины экрана isEqual() должна пользоваться функциямичленами height() и width() для чтения закрытых членов класса. Их реализациятривиальна:С++ для начинающихclass Screen {public:int height() { return _height; }int width() { return _width; }// ...private:short _heigh, _width;// ...};Применение оператора доступа к указателю на объект класса эквивалентнопоследовательному выполнению двух операций: применению оператора разыменования(*) к указателю, чтобы получить адресуемый объект, и последующему применениюоператора “точка” для доступа к нужному члену класса.
Например, выражениеs2->height()можно переписать так:(*s2).height()Результат будет одним и тем же.13.3. Функции-члены классаФункции-члены реализуют набор операций, применимых к объектам класса. Например,class Screen {public:void home() { _cursor = 0; }char get() { return _screen[_cursor]; }char get( int, int );void move( int, int );bool checkRange( int, int );int height() { return _height; }int width() { return _width; }// ...для Screen такой набор состоит из следующих объявленных в нем функций-членов:};Хотя у любого объекта класса есть собственная копия всех данных-членов, каждаяScreen myScreen, groupScreen;myScreen.home();функция-член существует в единственном экземпляре:groupScreen.home();При вызове функции home() для объекта myScreen происходит обращение к его члену_cursor.
Когда же эта функция вызывается для объекта groupScreen, то она обращается594С++ для начинающихк члену _cursor именно этого объекта, причем сама функция home() одна и та же. Какже может одна функция-член обращаться к данным-членам разных объектов? Для этогоприменяется указатель this, рассматриваемый в следующем разделе.13.3.1. Когда использовать встроенные функции-членыОбратите внимание, что определения функций home(), get(), height() и width()приведены прямо в теле класса.