- •Тема 1.Понятие технологии программирования (2 часа). 3
- •Тема 2. Основные концепции ооп (2 часа). 7
- •Тема 3. Конструкторы и деструкторы (2 часа). 12
- •Тема 5. Дружественные функции (friend functions) (2 часа) 32
- •Тема 6. Обработка исключительных ситуаций (2 часа) 44
- •Тема 8. Производные классы (2 часа) 76
- •Тема 9. Виртуальные функции (2 часа) 83
- •Тема 10. Множественное наследование. Производные классы векторов (2 часа) 90
- •Тема 12. Шаблоны функций и классов. 128
- •Тема 14. Применение оо-подхода в базах данных 148
- •Тема 1.Понятие технологии программирования (2 часа).
- •1.1. Предмет изучения курса ооп
- •1.2. Исторический экскурс
- •1.3. Основные технологии программирования
- •1.4. Заключение
- •Тема 2. Основные концепции ооп (2 часа).
- •2.1. Объекты и классы
- •2.1.1.Понятие класса объектов
- •2.1.2. Основные характеристики состояния класса
- •2.1.3. Понятие инкапсуляции свойств объекта
- •2.1.4. Структура глобальной памяти класса и глобальные методы класса
- •2.1.5. Интерфейс класса
- •2.1.6. Функции-члены класса
- •2.2. Понятие наследования (Inheritance)
- •2.3. Понятиеполиморфизма
- •Тема 3. Конструкторы и деструкторы (2 часа).
- •3.1. Для чего нужны конструкторы
- •3.2. Использование конструкторов «по умолчанию»
- •3.3. Использование деструкторов
- •3.4. Демонстрация последовательности работы конструкторов и деструкторов
- •3.5. Конструктор копирования
- •3.6. Определение операции присваивания
- •3.6.1. Пример использования конструктора копирования.
- •3.7.1. Краткий обзор библиотеки stl
- •3.7.2. Вектора
- •3.8. Inline-подстановка
- •4.1. Перегрузка операторов
- •4.1.1. Пример на перегрузку операторов
- •4.1.2. Общие принципы перегрузки операторов
- •4.1.3. Бинарные и Унарные Операции
- •4.2. Пример с перегрузкой операторов
- •Тема 5. Дружественные функции (friend functions) (2 часа)
- •5.1. Примеры использования дружественных функций
- •5.2. Особенности перегрузки префиксной и постфиксной форм унарных операций
- •5.3. Статические члены данных
- •5.4. Перегрузка операторов new, new[], delete, delete[]
- •Void* operator new(size_t размер){ код оператора
- •Void operator delete(void* p){ код оператора }
- •Void* operator new[](size_t размер){ код оператора return указатель_на_память; }
- •Void operator delete[](void* p){ код оператора }
- •Тема 6.Обработка исключительных ситуаций(2 часа)
- •6.1. Применение try, catch, throw
- •6.2. Синтаксис и семантика генерации и обработки исключений
- •6.3. Обработка исключений
- •6.4. Обработка исключений при динамическом выделении памяти
- •6.5. Функции, глобальные переменные и классы поддержки механизма исключений
- •6.6. Конструкторы и деструкторы в исключениях
- •7.1 Строковые типы
- •7.1.1. Преобразования, определяемые классом
- •7.1.2. Встроенный строковый тип
- •7.1.3 Класс string
- •7.2. Пример строкового класса с перегруженными операторами и дружественными функциями
- •Тема8.Производные классы (2 часа)
- •8.1. Определение производного класса
- •8.2. Правила использования атрбутов доступа
- •8.3. Конструкторы и деструкторы производных классов
- •Тема 9. Виртуальные функции (2часа)
- •9.1. Определение виртуальных методов
- •9.2. Абстрактные классы
- •9.3. Таблицы виртуальных методов (функций)
- •9.4. Выводы
- •Тема 10. Множественное наследование. Производные классы векторов (2 часа)
- •10.1. Множественное наследование
- •10.2. Отношения между классами
- •10.2.3. Ассоциация
- •10.2.4. Агрегирование
- •10.2.5. Наследование
- •10.3. Библиотека графических объектов (пример)
- •10.3.1. Динамический полиморфизм и наследование интерфейсов
- •10.3.2.Абстрактные классы
- •10.3.3. Множественное наследование в библиотеке графичкских фигур.
- •10.3.4. Иерархия классов библиотеки графичкских фигур
- •10.3.5. Таблица наследования
- •10.3.6. Диаграмма модулей
- •10.3.7.Директивы препроцессора
- •10.4. Производные классы векторов
- •10.5. Операции над векторами
- •11.1. Потоковый ввод-вывод
- •11.1.1. Классы потоков
- •11.1.2. Стандартные потоки
- •11.2.Опрос и установка состояния потока
- •11.3.Перегрузка операций извлечения и вставки в поток
- •11.4.Переадресация ввода-вывода
- •11.5. Операции помещения в поток и извлечения из потока
- •11.6.Форматирование потока
- •11.7.Файловый ввод-вывод с использованием потоков
- •11.8.Бесформатный ввод-вывод
- •11.9.Часто применяемые функции библиотеки ввода / вывода
- •11.10.Файлы с произвольным доступом
- •11.11. Буферизация
- •11.12. Заключение
- •Тема 12. Шаблоны функций и классов.
- •12.1 Шаблоны функций
- •12.2. Шаблоны классов
- •12.3. Размещение определений шаблонов в многомодульных программах
- •12.4. Полиморфные вектора
- •13.1 Область видимости
- •13.1.1. Локальная область видимости
- •13.2. Глобальные объекты и функции
- •13.2.1. Объявления и определения
- •13.2.2. Несколько слов о заголовочных файлах
- •13.3. Локальные объекты
- •13.3.1. Автоматические объекты
- •13.3.2. Регистровые автоматические объекты
- •13.3.3. Статические локальные объекты
- •13.4. Динамически размещаемые объекты
- •13.4.1. Динамическое создание и уничтожение единичных объектов
- •13.5. Определения пространства имен а
- •Тема 14. Применение оо-подхода в базах данных
- •14.1. Реляционные базы данных
- •14.2 Объектно-ориентированные базы данных (ообд)
- •14.3. Гибридные базы данных
- •Рекомендуемая литература
6.4. Обработка исключений при динамическом выделении памяти
Конкретные реализации компилятора языка C++ обеспечивают программиста некоторыми дополнительными возможностями для работы с исключениями. Здесь следует отметить предопределенные исключения, а также типы, переменные и функции, специально предназначенные для расширения возможностей механизма исключений.
Достаточно распространенной особой ситуацией, требующей специальных действий на этапе выполнения программы, является невозможность выделить нужный участок памяти при ее динамическом распределении. Стандартное средство для такого запроса памяти - это операция new или перегруженные операции, вводимые с помощью операций-функций operator new ()иoperator new [] (). По умолчанию, если операция new не может выделить требуемое количество памяти, то она возвращает нулевое значение (NULL) и одновременно формирует исключение типаxalloc. Кроме того, в реализацию ВС++ включена специальная глобальная переменная_new_handler, значением которой служит указатель на функцию, которая запускается на выполнение при неудачном завершении операции-функцииoperator new (). По умолчанию функция, адресуемая указателем_new_handler, завершает выполнение программы.
Функция set_new_handler ()позволяет программисту назначить собственную функцию, которая будет автоматически вызываться при невозможности выполнить операциюnew.
Функция set_new_handler ()принимает в качестве параметра указательmy_handlerна ту функцию, которая должна автоматически вызываться при неудачном выделении памяти операциейnew.
Параметр my_handlerспецифицирован как имеющий типnew_handler, определенный в заголовочном файлеnew.hтаким образом:
typedef void (new *new_handler) () throw (xalloc);
В соответствии с приведенным форматом new_handler- это указатель на функцию без параметров, не возвращающую значения (void) и, возможно, порождающую исключение типаxalloc. Типxalloc- это класс, определенный в заголовочном файлеexcept.h.
Объект класса xalloc, созданный как исключение, передает информацию об ошибке при обработке запроса на выделение памяти. Классxallocсоздан на базе классаxmsg, который выдает сообщение, определяющее сформированное исключение. Определениеxmsgв заголовочном файлеexcept.hвыглядит так:
class xmsg
{
public:
xmsg(const string &msg);
xmsg(const xmsg &msg);
~xmsg();
const string & why() const;
void raise() throw (xmsg);
xmsg operator =(const xmsg &src);
private:
string _FAR *str;
};
Класс xmsgне имеет конструктора по умолчанию. Общедоступный (public) конструктор:xmsg (string msg)предполагает, что с каждымxmsg-объектом должно быть связано конкретное явно заданное сообщение типаstring. Типstringопределен в заголовочном файлеcstring.h.
Общедоступные (public) компонентные функции класса:
void raise () throw (xmsg);
вызов raise ()приводит к порождению исключенияxmsg. В частности, порождается *this.
inline const string _FAR &xmsg::why() const
{
return *str;
};
выдает строку, использованную в качестве параметра конструктором класса xmsg. Поскольку каждый экземпляр (объект) классаxmsgобязан иметь собственное сообщение, все его копии должны иметь уникальные сообщения.
Класс xallocописан в заголовочном файлеexcept.hследующим образом:
class xalloc : public xmsg
{
public:
xalloc(const string &msg, size_t size); // Конструктор
size_t requested() const;
void raise() throw (xalloc);
private:
size_t siz;
};
Класс xallocне имеет конструктора по умолчанию, поэтому каждое определение объектаxallocдолжно включать сообщение, которое выдается в случае, если не может быть выделеноsizeбайт памяти. Типstringопределен в заголовочном файлеcstring.h.
Общедоступные (public) компонентные функции классаxalloc:
void xalloc::raise() throw (xalloc);
Вызов raise()приводит к порождению исключения типаxalloc. В частности, порождается *this.
inline size_t xalloc::requested() const
{
return siz;
}
Функция возвращает количество запрошенной для выделения памяти.
Если операция newне может выделить требуемого количества памяти, вызывается последняя из функций, установленных с помощьюset_new_handler(). Если не было установлено ни одной такой функции,newвозвращает значение0. Функцияmy_handler()должна описывать действия, которые необходимо выполнить, если new не может удовлетворить требуемый запрос.
Определяемая программистом функция my_handler()должна выполнить одно из следующих действий:
вызвать библиотечную функцию abort()илиexit();
передать управление исходному обработчику этой ситуации;
освободить память и вернуть управление программе;
вызвать исключение типа xallocили порожденное от него.
Рассмотрим особенности перечисленных вариантов. Вызов функции abort()рассматривался ранее.
Для демонстрации передачи управления исходному обработчику рассмотрим следующую программу.
#include <iostream.h> // Описание потоков ввода/вывода
#include <new.h> // Описание функции set_new_handler()
#include <stdlib.h> // Описание функции abort()
// Прототип функции - старого обработчика ошибок памяти:
void (*old_new_handler)();
void new_new-handler() // Функция для обработки ошибок
{
cerr << "Ошибка при выделении памяти!";
of (old_new_handler) (*old_new_handler)();
abort(); // "Abnormal program termination"
}
int main (void)
{
// Устанавливаем собственный обработчик ошибок;
old_new_handler = set_new_handler(new_new_handler);
// Цикл с ограничением количества попыток выделения памяти:
for (int n = 1; n <= 1000; n++)
{
cout << n << «: »;
new char [61440U]; // Пытаемся выделить 60 Кбайт
cout << "Успех!" << endl;
}
return 0;
}
При установке собственного обработчика ошибок адрес старого (стандартного) обработчика сохраняется как значение указателя old_new_handler. Этот сохраненный адрес используется затем в функции для обработки ошибокnew_new_handler. С его помощью вместо библиотечной функцииabort()вызывается "старый" обработчик.
Результаты выполнения программы в среде Windows:
1: Успех! . . . 244: Успех! Ошибка при выделении памяти!
и затем сообщение в окне: "Program Aborted".
Если my_handlerвозвращает управление программе,newпытается снова выделить требуемое количество памяти. Наилучшим выходом из ситуации нехватки памяти будет, очевидно, освобождение внутри функцииmy_handlerтребуемого количества памяти и возвращение управления программе. В этом случаеnewсможет удовлетворить запрос, и выполнение программы будет продолжено.
В следующем примере при нехватки памяти освобождаются блоки памяти, выделенной ранее, и управление возвращается программе. Для этого в программе определена глобальная переменная-указатель на блок (массив) символов (char *ptr).
#include <iostream.h> // Описание потоков ввода-вывода
#include <new.h> // Описание функции set_new_handler
char *ptr; // Указатель на блок (массив) символов
// Функция для обработки ошибок при выполнении операции new:
void new_new_handler()
{
cerr << "Ошибка при выделении памяти! ";
delete ptr; // Если выделить невозможно, удаляем последний блок
}
int main(void)
{
// Устанавливаем собственный обработчик ошибок:
set_new_handler (new_new_handler);
// Цикл с ограничением количества попыток выделения памяти:
for (int n = 1; n <= 1000; n++)
{
cout << n << ": ";
// Пытаемся выделить 60 Кбайт:
ptr = new char [61440U];
cout << "Успех! " << endl;
}
set_new_handler(0); // Отключаем все обработчики
return 0;
}
Результаты выполнения этой программы будет следующим (при запуске из командной строки DOS):
1: Успех!
. . .
6: Успех!
7: Ошибка при выделении памяти! Успех!
Если программа, выполняемая под Windows, не может получить требуемое количество памяти, то имеет смысл повторить попытки через некоторое время.
Если показанное в последнем примере освобождение памяти невозможно, функция my_handler()обязана либо вызвать исключение, либо завершить программу. В противном случае программа, очевидно, зациклится (после возврата изnew_new_handler()попытка new выделить память опять окончится неудачей, снова будет вызванаnew_new_handler(), которая, не очистив память, вновь вернет управление программе и т.д.)
Последняя из перечисленных задач, решаемых функцией, назначенной для обработки неудачного завершения операции new, предусматривает генерацию исключенияxalloc. Это исключение формирует и функция, которая по умолчанию обрабатывает неудачное завершение операцииnew. Рассмотрим на примере, какую информацию передает исключение типаxallocи как эту информацию можно использовать.
#include <except.h> // Описание класса xalloc
#include <iostream.h> // Описание потоков ввода/вывода
#include <cstring.h> // Описание класса string
int main (void)
{
try
{
for (int n = 1; n <= 1000; n++)
{
cout << n << ": ";
new char [61440U]; // Пытаемся выделить 60 Кбайт
cout << "Успех!" << endl;
}
}
catch (xalloc X)
{
cout << "При выделении памяти обнаружено ";
cout << "исключение "; << X.why();
}
return 0;
}
Результат выполнения программы (из командной строки DOS):
1: Успех!
. . .
6: Успех!
7: При выделении памяти обнаружено исключение Out of memory
К сожалению, стандартный обработчик ошибок выделения памяти не заносит количество не хватившей памяти в компоненте sizклассаxalloc, поэтому даже если в тело обработчика исключений в последнем примере вставить дополнительно вызов функцииrequested, возвращающейsiz, т.е.:
cout << "Обнаружено исключение " << X.why();
cout << " при выделении ";
cout << X.request() << "байт памяти";
то результат и в этом случае будет не очень информативным:
7: Обнаружено исключение Out of memory при выделении 0 байт памяти.
Самым радикальным способом устранения этой некорректности реализации будет, вероятно, перегрузка операции new. Покажем, как можно реализовать обработку ошибок операцииnewс помощью установки своей функции, которая будет порождать исключениеxallocс соответствующими значениями компонентов.
#include <except.h> // Описание класса xalloc
#include <iostream.h> // Описание потоков ввода/вывода
#include <new.h> // Описание функции set_new_handler
#include <cstring.h> // Описание класса string
#define SIZE 61440U
// Функция для обработки ошибок при выполнении операции new:
void new_new_handler () throw (xalloc)
{
// Если память выделить не удалось,
// формируем исключение xalloc с соответствующими компонентами:
throw (xalloc (string ("Memory full"), SIZE));
}
int main (void)
{
// Устанавливаем собственный обработчик ошибок:
set_new_handler (new_new_handler);
try // Контролируемый блок
{
for (int n = 1; n <= 1000; n++)
{
cout << n << "; ";
new char [SIZE]; // Пытаемся выделить 60 Кбайт
cout << "Успех! " << endl;
}
}
catch (xalloc X) // Обработчик исключений
{
cout << "Обнаружено исключение " << X.why ();
cout << "привыделении";
cout << X.requested () << "байт памяти. ";
}
return 0;
}
Результат выполнения программы (из командной строки DOS):
1: Успех!
. . .
7: Обнаружено исключение Memory full при выделении 61440 байт памяти.