
- •6.050201 «Системная инженерия»
- •Донецк, 2012
- •1 Цели и задачи дисциплины
- •2 Теоретические основы программирования
- •2.1 Основные сведения в области информатики Общее понятие алгоритма
- •Алгоритмические языки
- •Типы переменных
- •Целочисленные переменные
- •Кольцо вычетов по модулю m
- •Интерпретация положительных и отрицательных чисел
- •Вещественные переменные
- •Машинный эпсилон
- •Запись вещественных констант
- •Символьные переменные
- •Логические переменные и выражения
- •Массивы
- •Текстовые строки
- •Оперативная память
- •Процессор
- •Cisc и risc-процессоры
- •Алгоритм работы компьютера
- •Аппаратный стек
- •Команды вызова подпрограммы call и возврата return
- •Аппаратный стек и локальные переменные подпрограммы
- •2.2. Стандарты построения блок-схем алгоритмов
- •4 Компиляция и выполнение программ
- •5 Структурное программирование
- •5.1 Описание переменных
- •Константы
- •Целые числа
- •Вещественные числа
- •Логические величины
- •Символы и байты
- •Кодировка, многобайтовые символы
- •5.2 Основные операции и их приоритет
- •Порядок вычисления выражений
- •5.3 Операторы
- •Операторы цикла
- •5.4 Организация ввода-вывода
- •Манипуляторы и форматирование ввода-вывода
- •Строковые потоки
- •Ввод-вывод файлов
- •5.5 Массивы
- •5.6. Указатели и операции над ними
- •5.7 Ссылки
- •5.8 Динамическое выделение памяти
- •5.9 Функции
- •Подставляемые функции
- •Имена функций
- •Необязательные аргументы функций
- •Рекурсия
- •Назначение шаблонов
- •Функции-шаблоны
- •5.10 Область видимости имен
- •5.11 Сложные структуры данных
- •5.11.1 Структуры
- •5.11.2 Перечисления
- •5.11.3. Объединения
- •5.12. Динамические структуры данных
- •6 Препроцессор
- •Определение макросов
- •Условная компиляция
- •Дополнительные директивы препроцессора
- •7 Объектно-ориентированное программирование
- •7.1 Основные понятия объектно-ориентированного программирования
- •Определение методов класса
- •Виртуальные методы
- •Виртуальные методы и переопределение методов
- •Преобразование базового и производного классов
- •Внутреннее и защищенное наследование
- •Абстрактные классы
- •Множественное наследование
- •Виртуальное наследование
- •Интерфейс и состояние объекта
- •Объявление friend
- •7.2 Конструктор и деструктор класса
- •Копирующий конструктор
- •Деструкторы
- •Инициализация объектов
- •Операции new и delete
- •7.3 Перегрузка операций
- •Как определять операции
- •Преобразования типов
- •Явные преобразования типов
- •Стандартные преобразования типов
- •Преобразования указателей и ссылок
- •Преобразования типов, определенных в программе
- •7.4 Использование включаемых файлов
- •7.5. Шаблоны классов
- •"Интеллигентный указатель"
- •Задание свойств класса
- •8 Обработка исключительных ситуаций
- •Примеры обработки исключительных ситуаций
- •Список использованных источников
Виртуальное наследование
Базовый класс можно объявить виртуальным базовым классом, используя запись:
class Student : virtual Person
{
};
class Librarian : virtual Person
{
};
Гарантировано, что объект виртуального базового класса будет содержаться в объекте выведенного класса (см. рис. 6.4) один раз. Платой за виртуальность базового класса являются дополнительные накладные расходы при обращениях к его атрибутам и методам наследования.
Рис. 7.4 – Структура объекта StudentLibrarian при виртуальном множественном наследовании.
Интерфейс и состояние объекта
Основной характеристикой класса с точки зрения его использования является интерфейс, т.е. перечень методов, с помощью которых можно обратиться к объекту данного класса. Кроме интерфейса, объект обладает текущим значением или состоянием, которое он хранит в атрибутах класса. В Си++ имеются богатые возможности, позволяющие следить за тем, к каким частям класса можно обращаться извне, т.е. при использовании объектов, и какие части являются "внутренними", необходимыми лишь для реализации интерфейса.
Определение класса можно поделить на три части – внешнюю, внутреннюю и защищенную. Внешняя часть предваряется ключевым словом public , после которого ставится двоеточие. Внешняя часть – это определение интерфейса. Методы и атрибуты, определенные во внешней части класса, доступны как объектам данного класса, так и любым функциям и объектам других классов. Определением внешней части мы контролируем способ обращения к объекту. Предположим, мы хотим определить класс для работы со строками текста. Прежде всего, нам надо соединять строки, заменять заглавные буквы на строчные и знать длину строк. Соответственно, эти операции мы поместим во внешнюю часть класса:
class String
{
public:
// добавить строку в конец текущей строки
void Concat(const String& str);
// заменить заглавные буквы на строчные
void ToLower(void);
int GetLength(void) const;
// сообщить длину строки
. . .
};
Внутренняя и защищенная части класса доступны только при реализации методов этого класса. Внутренняя часть предваряется ключевым словом private, защищенная – ключевым словом protected.
class String
{
public:
// добавить строку в конец текущей строки
void Concat(const String& str);
// заменить заглавные буквы на строчные
void ToLower(void);
int GetLength(void) const;
// сообщить длину строки
private:
char* str;
int length;
};
В большинстве случаев атрибуты во внешнюю часть класса не помещаются, поскольку они представляют состояние объекта, и возможности их использования и изменения должны быть ограничены. Представьте себе, что произойдет, если в классе String будет изменен указатель на строку без изменения длины строки, которая хранится в атрибуте length.
Объявляя атрибуты str и length как private, мы говорим, что непосредственно к ним обращаться можно только при реализации методов класса, как бы изнутри класса (private по-английски – частный, личный). Например:
int
String::GetLength(void) const
{
return length;
}
Внутри определения методов класса можно обращаться не только к внутренним атрибутам текущего объекта, но и к внутренним атрибутам любых других известных данному методу объектов того же класса. Реализация метода Concat будет выглядеть следующим образом:
void
String::Concat(const String& x)
{
length += x.length;
char* tmp = new char[length + 1];
::strcpy(tmp, str);
::strcat(tmp, x.str);
delete [] str;
str = tmp;
}
Однако если в программе будет предпринята попытка обратиться к внутреннему атрибуту или методу класса вне определения метода, компилятор выдаст ошибку, например:
main()
{
String s;
if (s.length > 0) // ошибка
. . .
}
Разница между защищенными (protected) и внутренними атрибутами была описана в предыдущей лекции, где рассматривалось создание иерархий классов.
При записи классов мы помещаем первой внешнюю часть, затем защищенную часть и последней – внутреннюю часть. Дело в том, что внешняя часть определяет интерфейс, использование объектов данного класса. Соответственно, при чтении программы эта часть нужна прежде всего. Защищенная часть необходима при разработке зависимых от данного класса новых классов. И внутреннюю часть требуется изучать реже всего – при разработке самого класса.