- •Тема 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. Гибридные базы данных
- •Рекомендуемая литература
10.4. Производные классы векторов
Теперь рассмотрим более осмысленный пример на производные классы со встроенным типом вектор. Определим вектор, для которого пользователь может задавать границы изменения индекса.
class Vec: public vector
{int low, high; // числовые пределы вектора
public:
Vec(int,int);
int& elem(int);
int& operator[](int);}
;
Определение vec как :public vector
Тип vec имеет (наследует) все свойства типа vector дополнительно к тем, что описаны специально для него.
Класс vec модифицирует класс vector тем, что в нем задается другой конструктор, который требует от пользователя указывать две границы изменения индекса, а не длину, и имеются свои собственные функции доступа elem(int) и operator[](int). Функция elem() класса Vec легко выражается через elem() класса vector:
int& Vec::elem(int i)
{
return vector::elem(i-low);
}
С помощью унарной операции :: мы ссылаемся на нелокальное имя. Было бы разумно описать Vec::elem() как inline, поскольку, скорее всего, эффективность существенна, но необязательно, неразумно и невозможно написать ее так, чтобы она непосредственно использовала закрытый член v класса vector. Напомню, что функции производного класса не имеют доступа к закрытым членам его базового класса.
Конструктор можно написать так:
Vec::vec(int lb, int hb) : (hb-lb+1)
{
if (hb-lb<0) hb = lb;
low = lb;
high = hb;
}
Запись : (hb-lb+1) используется для определения списка параметров конструктора базового класса vector::vector(). Этот конструктор вызывается перед телом Vec::vec(). Вот небольшой пример, который можно запустить, если скомпилировать его вместе с остальными описаниями vector:
#include <streams.h>
void error(char* p)
{
cerr << p << "n\"; // cerr - выходной поток сообщений об ошибках
exit(1);
}
void vector::set_size(int) /* пустышка */
int& Vec::operator[](int i)
{
if (i<low || high<i) error("выход за границы vec");
return elem(i);
}
main()
{
vector a(10);
for (int i=0; i<a.size(); i++)
a[i] = i;
cout << a[i] << " ";
cout << "\n";
vec b(10,19);
for (i=0; i<b.size(); i++) b[i+10] = a[i];
for (i=0; i<b.size(); i++) cout << b[i+10] << " ";
cout << "\n";
}
Он выдает
0 1 2 3 4 5 6 7 8 9
0 1 2 3 4 5 6 7 8 9
Это направление развития векторного типа можно разрабатывать дальше. Довольно просто сделать многомерные массивы, массивы, в которых число размерностей задается как параметр конструктора, массивы в стиле Фортрана, к которым можно одновременно обращаться и как к имеющим две размерности, и как к имеющим три, и т.д.
Так класс управляет доступом к некоторым данным. Поскольку весь доступ осуществляется через интерфейс, обеспеченный открытой частью класса, то можно использовать представление данных в соответствие с нуждами разработчика.
10.5. Операции над векторами
Другое направление развития - снабдить вектора операциями:
class Vec : public vector
{
public:
Vec(int s) : (s)
Vec(Vec&);
~Vec()
void operator=(Vec&);
void operator*=(Vec&);
void operator*=(int);
//...
};
Обратите внимание на способ определения конструктора производного класса, Vec::Vec(), когда он передает свой параметр конструктору базового класса vector::vector() и больше не делает ничего. Это полезная парадигма. Операция присваивания перегружена, ее можно определить так:
void Vec::operator=(Vec& a)
{
int s = size();
if (s!=a.size()) error("плохой размер вектора для =");
for (int i = 0; i<s; i++) elem(i) = a.elem(i);
}
Присваивание объектов класса Vec теперь действительно копирует элементы, в то время как присваивание объектов vector просто копирует структуру, управляющую доступом к элементам. Последнее, однако, происходит и тогда, когда vector копируется без явного использования операции присваивания: (1) когда vector инициализируется посредством присваиваиня другого вектора; (2) когда vector передается как параметр; и (3) когда vector передается как значение, возвращаемое функцией. Чтобы обрабатывать эти случаи для векторов Vec, определяем конструктор Vec(Vec&):
Vec::Vec(Vec& a) : (a.size())
{
int sz = a.size();
for (int i = 0; i<sz; i++) elem(i) = a.elem(i);
}
Этот конструктор инициализирует Vec как копию другого Vec, и будет вызываться в отмеченных выше случаях.
Выражение в левой части таких операций, как = и +=, безусловно определено, поэтому кажется вполне естественным реализовать их как операции над объектом, который обозначается этим выражением. В частности, тогда они смогут изменять значение своего первого операнда. Левый операнд таких операций, как + и - не требует особого внимания. Вы могли бы, например, передавать оба аргумента по значению и все равно получить правильную рализацию векторного сложения. Однако вектора могут оказаться большими, поэтому чтобы избежать ненужного копирования операнды операции + передаются в operator+() по ссылке:
Vec operator+(Vec& a,Vec &b)
{
int s = a.size();
if (s != b.size()) error("плохой размер вектора для +");
Vec sum(s);
for (int i=0; i<s; i++)
sum.elem(i) = a.elem(i) + b.elem(i);
return sum;
}
Вот пример небольшой программы, которую можно выполнить, если скомпилировать ее вместе с ранее приведенными описаниями vector:
#include <stream.h>
{void error(char* p)
cerr << p << "\n";
exit(1);
}
void vector::set_size(int) /*...*/
int& Vec::operator[](int i) /*...*/
main()
{
Vec a(10);
Vec b(10);
for (int i=0; i<a.size(); i++) a[i] = i;
b = a;
Vec c = a+b;
for (i=0; i<c.size(); i++) cout << c[i] << "\n";
}
ТЕМА 11. ПОТОКИ. ОРГАНИЗАЦИЯ ВВОДА /ВЫВОДА В С++ (4 часа).