Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
otvety_po_OAiPR.doc
Скачиваний:
3
Добавлен:
01.03.2025
Размер:
1.23 Mб
Скачать

211. Виды классов:

Базовый (родительский)

Производный (наследник, потомок)

Абстрактный

Инетфейс

Механизм классов позволяет создавать новые типы данных; с его помощью введены типы string, vector, complex и pair, рассмотренные выше. В  мы рассказывали о концепциях и механизмах, поддерживающих объектный и объектно-ориентированный подход, на примере реализации класса Array. Здесь мы, основываясь на объектном подходе, создадим простой класс String, реализация которого поможет понять, в частности, перегрузку операций. (Классы подробно рассматриваются в главах 13, 14 и 15). Мы дали краткое описание класса для того, чтобы приводить более интересные примеры. Читатель, только начинающий изучение С++, может пропустить этот раздел и подождать более систематического описания классов в следующих главах.) Наш класс String должен поддерживать инициализацию объектом класса String, строковым литералом и встроенным строковым типом, равно как и операцию присваивания ему значений этих типов. Мы используем для этого конструкторы класса и перегруженную операцию присваивания. Доступ к отдельным символам String будет реализован как перегруженная операция взятия индекса. Кроме того, нам понадобятся: функция size() для получения информации о длине строки; операция сравнения объектов типа String и объекта String со строкой встроенного типа; а также операции ввода/вывода нашего объекта. В заключение мы реализуем возможность доступа к внутреннему представлению нашей строки в виде строки встроенного типа. Определение класса начинается ключевым словом class, за которым следует идентификатор – имя класса, или типа. В общем случае класс состоит из секций, предваряемых словами public (открытая) и private (закрытая). Открытая секция, как правило, содержит набор операций, поддерживаемых классом и называемых методами или функциями-членами класса. Эти функции-члены определяют открытый интерфейс класса, другими словами, набор действий, которые можно совершать с объектами данного класса. В закрытую секцию обычно включают данные-члены, обеспечивающие внутреннюю реализацию. В нашем случае к внутренним членам относятся _string – указатель на char, а также _size типа int. _size будет хранить информацию о длине строки, а _string – динамически выделенный массив символов. Вот как выглядит определение класса:

#include <iostream>

class String;

istream& operator>>( istream&, String& ); ostream& operator<<( ostream&, const String& );

class String { public: // набор конструкторов // для автоматической инициализации // String strl; // String() // String str2( "literal" ); // String( const char* ); // String str3( str2 ); // String( const String& );

String(); String( const char* ); String( const String& );

// деструктор ~String();

// операторы присваивания // strl = str2 // str3 = "a string literal"

String& operator=( const String& ); String& operator=( const char* );

// операторы проверки на равенство // strl == str2; // str3 == "a string literal";

bool operator==( const String& ); bool operator==( const char* );

// перегрузка оператора доступа по индексу // strl[ 0 ] = str2[ 0 ];

char& operator[]( int );

// доступ к членам класса int size() { return _size; } char* c_str() { return _string; }

private: int _size; char *_string; }

Класс String имеет три конструктора. Как было сказано в разделе 2.3, механизм перегрузки позволяет определять несколько реализаций функций с одним именем, если все они различаются количеством и/или типами своих параметров. Первый конструктор String(); является конструктором по умолчанию, потому что не требует явного указания начального значения. Когда мы пишем: String str1; для str1 вызывается такой конструктор. Два оставшихся конструктора имеют по одному параметру. Так, для String str2("строка символов"); вызывается конструктор String(const char*); а для String str3(str2); конструктор String(const String&); Тип вызываемого конструктора определяется типом фактического аргумента. Последний из конструкторов, String(const String&), называется копирующим, так как он инициализирует объект копией другого объекта. Если же написать: String str4(1024); то это вызовет ошибку компиляции, потому что нет ни одного конструктора с параметром типа int.

212. Класс - это тип. Этот производный тип вводится в программу с помощью специального оператора объявления класса. В объявлении класса используется ранее описанный инструментальный набор средств для построения и преобразования производных типов.

Очередное множество форм Бэкуса-Наура определяет синтаксис объявления класса.

Объявление ::= [СписокСпецификаторовОбъявления] [СписокОписателей];

СписокСпецификаторовОбъявления

::= [СписокСпецификаторовОбъявления] СпецификаторОбъявления

СпецификаторОбъявления ::= СпецификаторТипа

::= *****

СпецификаторТипа ::= СпецификаторКласса

::= УточнённыйСпецификаторТипа

::= *****

УточнённыйСпецификаторТипа ::= КлючевоеСловоКласса ИмяКласса

::= КлючевоеСловоКласса Идентификатор

::= enum ИмяПеречисления

КлючевоеСловоКласса ::= union

::= struct

::= class

ИмяКласса ::= Идентификатор

СпецификаторКласса ::= ЗаголовокКласса {[СписокЧленов]}

ЗаголовокКласса

::= КлючевоеСловоКласса [Идентификатор] [СпецификацияБазы]

::= КлючевоеСловоКласса ИмяКласса [СпецификацияБазы]

КлючевоеСловоКласса ::= union

::= struct

::= class

ИмяКласса ::= Идентификатор

Спецификатор класса представляет то, что называется объявлением класса. Уточнённый спецификатор типа объявляет расположенный за ним идентификатор именем класса. Уточнённый спецификатор обеспечивает неполное предварительное объявление класса и перечисления.

Назначение и смысл необязательного нетерминального символа СпецификацияБазы будут обсуждаться позже, в разделах, посвящённых наследованию.

Предварительное объявление обеспечивается уточнённым спецификатором типа и является своеобразным прототипом класса или перечисления. Его назначение - сообщение транслятору предварительной информации о том, что существует (должно существовать) объявление класса (или перечисления) с таким именем. Идентификатор, используемый в контексте уточнённого спецификатора имени становится именем класса (или именем перечисления).

Класс считается объявленным даже тогда, когда в нём полностью отсутствует информация о членах класса (пустой список членов класса). Неименованный класс с пустым множеством членов - уже класс!

Имя класса можно употреблять как имя (имя типа) уже в списке членов этого самого класса.

Класс может быть безымянным.

Следующая последовательность операторов объявления

class {}; /* Объявлен пустой неименованный класс.*/

class {};

class {};

class {};

/* Это всё объявления. Их количество ничем не ограничивается. */

struct {};

/* Структура - это класс, объявленный с ключевым словом struct.

Опять же пустой и неименованный.*/

не вызывает у транслятора никаких возражений.

На основе класса, пусть даже неименованного, может быть объявлен (вернее, определён) объект-представитель этого класса. В таком контексте объявление неименованного (пусть даже и пустого!) класса является спецификатором объявления. Имена определяемых объектов (возможно с инициализаторами) составляют список описателей.

class {} Obj1, Obj2, Obj3;/* Здесь объявление пустого класса.*/

class {} Obj4, Obj5, Obj6;/* Просто нечего инициализировать.*/

class {} Obj1;

/* ^ Ошибка. Одноименные объекты в области действия имени.*/

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

Сочетание спецификатора typedef с объявлением безымянного класса подобно объявлению класса с именем:

class MyClass {/*…*/};

typedef class {/*…*/} MyClass;

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

class {} Obj1;

MyClass Obj1;

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

213-214. Свойства классов (property) – это атрибут формы или другого компонента, который влияет либо на визуальное поведение, либо на операции, выполняемые формой или компонентами. Фактически свойство – это просто имя, связанное с полем напрямую или методами записи и/или чтения и наиболее видимая часть класса.

При использовании в этом точном техническом значении, термин обозначает расширение концепции поля данных. Свойство является элементом определения класса и обеспечивает специальную защиту связанных с ним данных, поддерживая их автоматическую настраиваемую обработку при просмотре и изменении значений. Другими словами, свойство не являются только данными, которые присваиваются и используются; присвоение и/или использование этих данных может привести к особым побочным эффектам. Это один из часто используемых вариантов реализации инкапсуляции.

Синтаксис объявления свойства класса:

Property <имя свойства>: <тип данных> [Index <целое число>} [Read <поле свойства\метод чтения>] [Write <поле свойства\метод записи>} [Stored <логическое выражение>] [Default <значение по умолчанию>|NoDefault] [DispID <целое число>] [Implements <список интерфейсов>

Примечания: • Delphi позволяет объявлять свойства следующих типов: • простые типы, включая целые и вещественные числа, например Width, Height, символьные, перечислимые (например цвет формы, компонентов), логические (False, True) и диапазоны; • строковые, например имена компонентов (Name), заголовки (Caption), значения (Text): ✓ множества, например Borderlcons (biSystemMenu, biMinimize, biMaximize, biHelp). Раз-мер публикуемых (Published) свойств типа множество ограничен 32 элементами (0..31). С большим числом элементов можно объявить свойство в разделе Public; ✓ массивы, включая многомерные, например Lines (TStrings). Нельзя объявлять такие свойства в разделе Published. Массивы обычно имеют встроенные редакторы для изменения значений; ✓ указатели; • Объявление свойства никогда не приводит к резервированию памяти для хранения значений свойства, поскольку экземпляр класса хранит значение свойства в одном из соответствующих своих полей. • Свойства должны объявляться после объявления полей, можно вперемежку с методами. Главное, чтобы упоминаемые в разделах Read и Write методы были описаны ранее. • Поля и/или методы доступа могут быть унаследованными, но видимыми потомком. • Свойства, как и поля, могут использоваться в качестве аргументов процедур и функций, а также участвовать в выражениях, например логических, математических. Нельзя, однако, передавать их по ссылке (как Var параметр), • Свойство должно иметь хотя бы один из разделов Read или Write. Если раздел Read отсутствует, то свойство доступно только для записи, например, для хранения пароля, а, если отсутствует раздел Write, то свойство годится только для чтения и его нельзя модифицировать. • Если потребности в специальных методах нет, то для доступа к полям можно вместо имен методов использовать имена полей. Чаще всего так делают для раздела Read. • Методы чтения и записи рекомендуется объявлять в разделе Protected, чтобы их нельзя было неосторожно изменить, а также виртуальными или динамическими, чтобы была возможность переопределить их в потомках. Методы, однако, не должны быть абстрактными (Abstract) или перегружаемыми (Overload). • Если в разделах Read и Write указаны методы, то типы данных поля и свойства могут не совпадать. В этом случае соответствующие преобразования типов должны производится в методах чтения и записи. • Свойства, как и другие составляющие класса, наследуются потомками.

Важное значение имеют свойства при разработке компонентов. Разработчик может менять реализацию методов, а для пользователей все остается по старому.

В простейшем случае может использоваться прямой доступ к полю. Чаще всего прямой доступ используется при операциях чтения значения поля.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]