- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
- •Оглавление
- •Предисловие
- •Введение
- •1.1. Понятие класса и объекта. Инкапсуляция
- •1.2. Определение классов. Компоненты. Доступность
- •Class_key /*class_id*/ { /*members_list*/ };
- •Value_type class_id::function_id(parameters) {statements}
- •CPoint point1(100,70); // локальный объект
- •Static cPoint point3(50,120); // статический объект
- •Class_id(parameters) /*:initializer_list*/ {/*statements*/}
- •CString(const char *);
- •Delete[] __thematrix;
- •1.4. Обращение к компонентам объектов
- •1.5. Статические и нестатические компоненты классов
- •1.7. Указатель this
- •В опросы для самопроверки
- •2. Механизм наследования. Полиморфизм
- •2.1. Формы наследования. Базовые и производные классы
- •Class_key class_id: inheritance_specifier base_class_id {member_list};
- •2.3. Абстрактные классы
- •2.4. Множественное наследование и виртуальные классы
- •2.5. Преобразование динамических типов. Динамическая идентификация типов
- •Catch ( std::bad_cast & ) { // обработка исключения
- •Return 0;
- •Вопросы для самопроверки
- •3. Дружественные функции и классы
- •3.1. Дружественные функции
- •3.2. Дружественные классы
- •Вопросы для самопроверки
- •4. Механизм вложения
- •4.1. Вложенные классы
- •4.2. Локальные классы
- •Вопросы для самопроверки
- •5. Объектная модель и шаблоны
- •5.1. Определение, описание и инстанцирование шаблонов
- •::Function_id(function_parameter_list) { statements }
- •5.2. Параметры и аргументы шаблонов
- •Class identifier typename identifier
- •// Key, Data – параметры-типы (типы ключа и данных отображения)
- •// Container – контейнер, где содержится информация отображения class сMap {
- •Class MyTemplate
- •Int array[10]; struct Structure { int m; static int sm; } str;
- •5.3. Шаблоны компонентных функций
- •Value_type function_template_id(function_parameter_list) { statements }
- •::Function_template_id(function_parameter_list) { statements }
- •5.4. Специализация шаблонов
- •Вопросы для самопроверки
- •6. Перегрузка операций
- •Value_type operator @ (parameter_list);
- •Value_type operator @ (parameter_list) { statements }
- •Return fail();
- •6.3. Перегрузка бинарных операций
- •Value_type operator @ (parameter); // компонентная функция
- •Value_type operator @ (parameter, parameter); // глобальная функция friend value_type operator @ (parameter, parameter); // дружественная функция
- •Return *this;
- •Return *this;
- •/* Присваиваем собственные данные класса d */
- •6.4. Перегрузка операций управления памятью
- •Typedef void (*new_handler) ();
- •Extern new_handler set_new_handler( new_handler new_p );
- •Void operator delete(void * memory) {
- •... // Специальная обработка пользователя ::operator delete(memory); // освободить память
- •Вопросы для самопроверки
- •7. Механизм исключений
- •Throw expression
- •7.3. Специальные средства поддержки механизма исключений
- •Unexpected_function set_unexpected(unexpected_function func_name);
- •Typedef void (* unexpected_function) ();
- •Extern char * __throwExceptionName; extern char * __throwFileName; extern unsigned __throwLineNumber;
- •Вопросы для самопроверки
- •8. Подсчет ссылок
- •8.1. Назначение механизма подсчета ссылок
- •8.2. Контекстно-независимая модель счетчика ссылок
- •8.4. Внедрение подсчета ссылок в существующий класс
- •Вопросы для самопроверки
- •9. Стандартная библиотека шаблонов (stl)
- •9.1. Назначение и архитектура stl
- •9.2. Последовательные контейнеры
- •Class vector {
- •// Определение итераторов
- •Sort(first,last); // сортировка вектора в диапазоне итераторов
- •Ifstream ifile ("example.In"); ofstream ofile ("example.Out");
- •OutputIterator copy(
- •InputIterator first, InputIterator last, OutputIterator result );
- •// Заполнение списка
- •Operator- (int)
- •Operator- (random access iterator) operator[] (int)
- •InputIterator find(InputIterator first, InputIterator last, const t & value);
- •InputIterator find(InputIterator first, InputIterator last, const t & value)
- •Return first;
- •OutputIterator copy (InputIterator first, InputIterator last, OutputIterator result)
- •Return result;
- •OutputIterator transform (InputIterator first, InputIterator last, OutputIterator result, UnaryOperation op)
- •Return result;
- •Void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp)
- •__Quick_sort_loop(first, last, comp); __final_insertion_sort(first, last, comp);
- •T accumulate(InputIterator first, InputIterator last, t init, Function f);
- •V.Push_back(2); V.Push_back(5);
- •9.5. Функторы
- •T operator()(const t & X) const { return -X; }
- •9.7. Адаптеры
- •S1.Push(1); s1.Push(5);
- •// Записать в вектор числа 1 2 3 4
- •// Сортировать по неубыванию
- •// Записать в вектор числа 4 6 10 3 13 2
- •Вопросы для самопроверки
- •Заключение
- •Библиографический Список
- •6Vpj7-h3cxh-hbtpt-x4t74-3yvy7
2.3. Абстрактные классы
Поддержка механизма абстрактных классов в языке С++ преследует две цели. Во-первых, такие классы позволяют моделировать абстрактные понятия. Примером абстрактного класса может служить класс произвольных геометрических фигур CShape. У этого класса есть по меньшей мере один аспект поведения, который нельзя представить иначе как в обобщенной форме, – это изображение фигуры. Нарисовать фигуру, не зная закон ее построения, нельзя. Во-вторых, абстрактные классы могут быть использованы для описания абстрактных наборов функциональных возможностей, называемых интерфейсами. Интерфейс лишь определяет некоторый «срез» функциональности, а производные от этого интерфейса классы дают свои варианты его реализации. Примером может быть интерфейс IDispatch, который унифицирует взаимодействие объектов-клиентов и объектов-серверов в рамках технологии COM12 корпорации Microsoft. IDispatch только задает состав функций (методов) для взаимодействия, а их реализацию дает производный класс (например, в ATL13 данный интерфейс по умолчанию реализуется шаблонным классом IDispatchImpl).
Формально в С++ под абстрактным классом понимается любой класс, который содержит хотя бы одну чисто виртуальную функцию (такая функция может быть явно введена в текущем классе или унаследована от другого класса). Чисто виртуальная функция – это компонентная функция со спецификатором virtual, которая представляет абстрактный аспект поведения класса. Формат записи ее прототипа следующий:
virtual value_type function_id(parameter_list) = 0;
где value_type, function_id, parameter_list – соответственно тип значения, идентификатор и список параметров функции. Символы = 0 представляют так называемый «пустой» спецификатор (pure-specifier). Именно он сообщает компилятору о том, что функция является чисто виртуальной.
Чисто виртуальная функция декларирует некоторый аспект поведения, который можно реализовать в текущем классе лишь обобщенно. Однако он должен быть полностью реализован в одном из производных классов. Для этого производный класс переопределяет данную функцию (уже без пустого спецификатора) и определяет ее тело. Следует отметить, что если класс просто наследует чисто виртуальную функцию без определения, то он также становится абстрактным.
Использование механизма абстрактных классов требует выполнения ряда требований и ограничений. Некоторые из них даны ниже.
1. Если класс является абстрактным, то создать экземпляр этого класса можно лишь в одном случае: когда этот экземпляр есть часть объекта производного класса. Создать автономный объект абстрактного класса нельзя.
2. Чисто виртуальная функция может иметь реализацию. Реализация при этом должна быть во внешнем определении функции. Чисто виртуальный деструктор обязательно должен иметь реализацию, даже если он ничего не делает14.
3. Абстрактный класс не может специфицировать параметр функции, тип возвращаемого значения функции. Также его нельзя использовать при явном преобразовании типа.
4. Абстрактный класс может быть получен из неабстрактного класса путем введения новой, чисто виртуальной функции. Чисто виртуальная функция может «переопределять» обычную виртуальную функцию.
Ниже дан пример использования механизма абстрактных классов при построении классовой иерархии (в качестве примера выбрана уже рассмотренная ранее иерархия классов геометрических фигур, которая модифицирована с учетом возможностей абстрактных классов).
Пример
class CCenteredShape // произвольная геометрическая фигура с центром
{
public:
CCenteredShape (unsigned x = 0, unsigned y = 0): __x(x), __y(y) {}
void PrintCenter() const // печать координат центра фигуры
{
cout << xC << " " << yC << endl;
}
virtual void Draw() const = 0;
// рисование фигуры – чисто виртуальная функция
protected:
unsigned __x, __y;
};
class CFilledCenteredShape: public CCenteredShape
// закрашенная геометрическая фигура (тоже абстрактный класс)
{
public:
typedef /* ... */ fillcolor_t; // тип цвета заливки
CFilledCenteredShape(unsigned x, unsigned y, fillcolor_t c = clblack):
CCenteredShape(x,y), __fillcolor(c) {}
// функция рисование фигуры Draw не переопределяется
protected:
fillcolor_t __fillcolor; // цвет заливки
};
class CCircle: public CCenteredShape // окружность – неабстрактный класс
{
public:
CCircle(unsigned x, unsigned y, unsigned r):
CCenteredShape(x,y), __radius(r) {}
void Draw() const; // переопределение чисто виртуальной функции
double Area() const { return M_PI * __radius * __radius; }
protected:
unsigned __radius;
};
В данном примере базовым классом является CCenteredShape – класс произвольных геометрических фигур с центром. Он абстрактный, так как включает чисто виртуальную функцию Draw. У класса CCenteredShape два производных класса: первый (CFilledCenteredShape) моделирует закрашенные фигуры с центром, второй (CCircle) представляет незакрашенные окружности. Класс CFilledCenteredShape абстрактный, так как наследует от CCenteredShape чисто виртуальную функцию Draw и не обеспечивает ее переопределение без пустого спецификатора. Класс CCircle не будет абстрактным, поскольку дает функции Draw свой вариант реализации. От класса CCircle можно определять автономные объекты.
Анализируя наследование обычных, виртуальных и чисто виртуальных функций, можно сделать важные выводы, которые помогут правильно определить содержание классов при проектировании. Когда наследуется обычная (невиртуальная) компонентная функция, это означает, что производный класс таким же образом реализует соответствующий аспект поведения, как и базовый класс. Невиртуальные компонентные функции представляют собой инвариант относительно специализации классов. Например, класс CCircle наследует от базового класса CCenteredShape невиртуальную функцию PrintCenter, поскольку вывод координат центра выполняется в этих классах единообразно. Попытки переопределять невиртуальные функции не считаются компилятором ошибкой, но нарушают логику и снижают качество проекта. Когда наследуется виртуальная функция, это значит, что производный класс может реализовать соответствующий аспект поведения другим способом. Если способы реализации идентичны, то функция не переопределяется (говорят, что наследуется как интерфейс, так и необязательная реализация функции). Если же производному классу надо по-другому осуществить рассматриваемый аспект поведения, то он переопределяет функцию (или наследует только интерфейс функции, переопределяя при этом ее реализацию). И наконец, когда наследуется чисто виртуальная функция, это означает, что производный класс обязан предоставить реализацию для функции, в противном случае он также будет абстрактным (говорят, что наследуется только интерфейс функции, но у каждого неабстрактного класса свой способ реализации функции).
Чисто виртуальные функции нельзя вызывать, исключение составляет лишь следующий случай. Он относится к вызову чисто виртуальных деструкторов. Особенность механизма деструкторов состоит в том, что в классовой иерархии при уничтожении объектов должны вызываться все деструкторы, начиная деструктором текущего класса объекта до деструктора самого общего базового класса (об этом мы уже упоминали в п.2.1). Для успешного выполнения вызова деструктор должен иметь реализацию. Если деструктор чисто виртуальный, то он также снабжается пустой реализацией. Определение чисто виртуального деструктора нужно лишь для того, чтобы обеспечить уничтожение части объекта, соответствующей базовому абстрактному классу. Делать деструктор чисто виртуальным нужно не всегда, а лишь в том случае, когда текущий класс должен быть абстрактным, но выбрать функцию на роль чисто виртуальной не удается. Тогда деструктор и делается чисто виртуальным.