С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 101
Текст из файла (страница 101)
Завершается главаизложением правил разрешения перегрузки функций с учетом аргументов типа класса,функций-членов и перегруженных операторов.Тема главы 16 – шаблоны классов. Шаблон – это предписание для создания класса, вкотором один или несколько типов параметризованы. Например, vector может бытьпараметризован типом элементов, хранящихся в нем, а buffer – типом элементов вбуфере или его размером. В этой главе объясняется, как определить и конкретизироватьшаблон. Поддержка классов в C++ теперь рассматривается иначе – в свете наличияшаблонов, и снова обсуждаются функции-члены, объявления друзей и вложенные типы.Здесь мы еще раз вернемся к модели компиляции шаблонов, описанной в главе 10, чтобыпоказать, какое влияние оказывают на нее шаблоны классов.С++ для начинающих5841313.
КлассыМеханизм классов в C++ позволяет пользователям определять собственные типыданных. По этой причине их часто называют пользовательскими типами. Классможет наделять дополнительной функциональностью уже существующий тип. Так,например, IntArray, введенный в главе 2, предоставляет больше возможностей, чемтип “массив int”. С помощью классов можно создавать абсолютно новые типы,например Screen (экран) или Account (расчетный счет). Как правило, классыиспользуются для абстракций, не отражаемых встроенными типами адекватно.В этой главе мы узнаем, как определять типы и использовать объекты классов;увидим, что определение класса вводит как данные-члены, описывающие его, так ифункции-члены, составляющие набор операций, применимых к объектам класса.Мы покажем, как можно обеспечить сокрытие информации, объявив внутреннеепредставление и реализацию закрытыми, но открыв операции над объектами.Говорят, что закрытое внутреннее представление инкапсулировано, а открытуючасть класса называют его интерфейсом.Далее в этой главе мы познакомимся с особым видом членов класса – статическимичленами.
Мы расскажем также, как можно использовать указатели на члены ифункции-члены класса, и рассмотрим объединения, представляющие собойспециализированный вид класса для хранения объектов разных типов в однойобласти памяти. Завершается глава обсуждением области видимости класса иописанием правил разрешения имен в этой области; затрагиваются такие понятия,как вложенные классы, классы-члены пространства имен и локальные классы.13.1. Определение классаОпределение класса состоит из двух частей: заголовка, включающего ключевое словоclass, за которым следует имя класса, и тела, заключенного в фигурные скобки. Послеclass Screen { /* ...
*/ };такого определения должны стоять точка с запятой или список объявлений:class Screen { /* ... */ } myScreen, yourScreen;Внутри тела объявляются данные-члены и функции-члены и указываются уровни доступак ним. Таким образом, тело класса определяет список его членов.Каждое определение вводит новый тип данных. Даже если два класса имеют одинаковыесписки членов, они все равно считаются разными типами:С++ для начинающих585class First {int memi;double memd;};class Second {int memi;double memd;};class First obj1;Second obj2 = obj1;// ошибка: obj1 и obj2 имеют разные типыТело класса определяет отдельную область видимости.
Объявление членов внутри телапомещает их имена в область видимости класса. Наличие в двух разных классах членов содинаковыми именами – не ошибка, эти имена относятся к разным объектам. (Подробнееоб областях видимости классов мы поговорим в разделе 13.9.)После того как тип класса определен, на него можно ссылаться двумя способами:•написать ключевое слово class, а после него – имя класса. В предыдущем примереобъект obj1 класса First объявлен именно таким образом;•указать только имя класса. Так объявлен объект obj2 класса Second из приведенногопримера.Оба способа сослаться на тип класса эквивалентны.
Первый заимствован из языка C иостается корректным методом задания типа класса; второй способ введен в C++ дляупрощения объявлений.13.1.1. Данные-членыДанные-члены класса объявляются так же, как переменные. Например, у класса Screen#include <string>class Screen {stringstring::size_typeshortshort_screen;_cursor;_height;_width;////////string( _height * _width )текущее положение на экранечисло строкчисло колонокмогут быть следующие данные-члены:};Поскольку мы решили использовать строки для внутреннего представления объектакласса Screen, то член _screen имеет тип string. Член _cursor – это смещение встроке, он применяется для указания текущей позиции на экране.
Для него использованпереносимый тип string::size_type. (Тип size_type рассматривался в разделе 6.8.)Необязательно объявлять два члена типа short по отдельности. Вот объявление классаScreen, эквивалентное приведенному выше:С++ для начинающих586class Screen {/** _ screen адресует строку размером _height * _width* _cursor указывает текущую позицию на экране* _height и _width - соответственно число строк и колонок*/string_screen;string::size_type _cursor;short_height, _width;};class StackScreen {int topStack;void (*handler)();vector<Screen> stack;// указатель на функцию// вектор классовЧлен класса может иметь любой тип:};Описанные данные-члены называются нестатическими.
Класс может иметь также истатические данные-члены. (У них есть особые свойства, которые мы рассмотрим вразделе 13.5.)Объявления данных-членов очень похожи на объявления переменных в областивидимости блока или пространства имен. Однако их, за исключением статическихclass First {intmemi = 0;double memd = 0.0;// ошибка// ошибкачленов, нельзя явно инициализировать в теле класса:};Данные-члены класса инициализируются с помощью конструктора класса. (Мырассказывали о конструкторах в разделе 2.3; более подробно они рассматриваются вглаве 14.)13.1.2.
Функции-членыПользователям, по-видимому, понадобится широкий набор операций над объектами типаScreen: возможность перемещать курсор, проверять и устанавливать области экрана ирассчитывать его реальные размеры во время выполнения, а также копировать одинобъект в другой. Все эти операции можно реализовать с помощью функций-членов.Функции-члены класса объявляются в его теле.
Это объявление выглядит точно так же,как объявление функции в области видимости пространства имен. (Напомним, чтоглобальная область видимости – это тоже область видимости пространства имен.Глобальные функции рассматривались в разделе 8.2, а пространства имен – в разделе8.5.) Например:С++ для начинающихclass Screen {public:void home();void move( int, int );char get();char get( int, int );void checkRange( int, int );// ...};class Screen {public:// определения функций home() и get()void home() { _cursor = 0; }char get() { return _screen[_cursor]; }// ...Определение функции-члена также можно поместить внутрь тела класса:};home() перемещает курсор в левый верхний угол экрана; get() возвращает символ,находящийся в текущей позиции курсора.Функции-члены отличаются от обычных функций следующим:•функция-член объявлена в области видимости своего класса, следовательно, ее имя невидно за пределами этой области.
К функции-члену можно обратиться с помощьюptrScreen->home();одного из операторов доступа к членам – точки (.) или стрелки (->):myScreen.home();(в разделе 13.9 область видимости класса обсуждается более детально);•функции-члены имеют право доступа как к открытым, так и к закрытым членамкласса, тогда как обычным функциям доступны лишь открытые. Конечно, функциичлены одного класса, как правило, не имеют доступа к данным-членам другогокласса.Функция-член может быть перегруженной (перегруженные функции рассматриваются вглаве 9).
Однако она способна перегружать лишь другую функцию-член своего класса.По отношению к функциям, объявленным в других классах или пространствах имен,функция-член находится в отдельной области видимости и, следовательно, не можетперегружать их. Например, объявление get(int, int) перегружает лишь get() из тогоже класса Screen:587С++ для начинающих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)static const int_height = 24;static const int_width = 80;string_screen;string::size_type _cursor;эффективным:};Прежняя реализация функций-членов (то, как они манипулируют данными-членамикласса) больше не годится, ее нужно переписать. Но это не означает, что долженизмениться и интерфейс функций-членов (список формальных параметров и типвозвращаемого значения).Если бы данные-члены класса Screen были открыты и доступны любой функции внутрипрограммы, как отразилось бы на пользователях изменение внутреннего представленияэтого класса?•все функции, которые напрямую обращались к данным-членам старогопредставления, перестали бы работать.
Следовательно, пришлось бы отыскивать иизменять соответствующие части кода;•так как интерфейс не изменился, то коды, манипулировавшие объектами классаScreen только через функции-члены, не пришлось бы модифицировать. Но посколькусами функции-члены все же изменились, программу пришлось бы откомпилироватьзаново.Сокрытие информации – это формальный механизм, предотвращающий прямой доступ квнутреннему представлению типа класса из функций программы. Ограничение доступа кчленам задается с помощью секций тела класса, помеченных ключевыми словамиpublic, private и protected – спецификаторами доступа. Члены, объявленные всекции public, называются открытыми, а объявленные в секциях private и protectedсоответственно закрытыми или защищенными.588С++ для начинающих•открытый член доступен из любого места программы.