15.3. Данные класса
Конструкторы и деструкторы
Конструктор (constructor) — это подпрограмма, которая вызывается, когда создается объект класса; когда объект уничтожается, вызывается деструктор (destructor). Фактически, каждый объект (переменная), определенный в каком-либо языке, требует выполнения некоторой обработки при создании и уничтожении переменной хотя бы для выделения и освобождения памяти. В объектно-ориентированных языках программист может задать такую обработку.
Конструкторы и деструкторы в языке C++ могут быть определены для любого класса; фактически, если вы не определяете их сами, компилятор обеспечит предусмотренные по умолчанию. Синтаксически конструктор — это подпрограмма с именем класса, а деструктор — то же имя с префиксным символом «~»:
class Airplanes {
private:
C++ |
int current_airplanes;
public:
Airplanes(int i = 0): current_airplanes(i) {};
~Airplanes();
};
После создания базы данных Airplanes число самолетов получает значение параметра i, который по умолчанию имеет значение ноль:
Airplanes а1 (15); // current_airplanes =15
Airplanes a2; //current_airplanes = О
Когда база данных удаляется, будет выполнен код деструктора (не показанный). Можно определить несколько конструкторов, которые перегружаются на сигнатурах параметров:
class Airplanes {
public:
Airplanes(int i = 0): current_airplanes(i) {};
C++ |
~Airptartes();
};
Airplanes a3(5,6); // current_airplanes = 11
В языке C++ также есть конструктор копирования (copy constructor), который дает возможность программисту задать свою обработку для случая, когда объект инициализируется значением существующего объекта или, в более общем случае, когда один объект присваивается другому. Полное определение конструкторов и деструкторов в языке C++ довольно сложное; более подробно см. гл. 12 справочного руководства по языку C++.
В языке Ada 95 явные конструкторы и деструкторы обычно не объявляются. Для простой инициализации переменных достаточно использовать значения по умолчанию для полей записи:
type Airplanes is tagged
record
Current_Airplanes: Integer := 0;
end record;
Ada |
или дискриминанты (см. раздел 10.4):
type Airplanes(lnitial: Integer) is tagged
record
Current_Airplanes: Integer := Initial;
end record;
Программист может определить свои обработчики, порождая тип из абстрактного типа, называемого управляемым (Controlled). Этот тип обеспечивает абстрактные подпрограммы для Инициализации (Initialization), Завершения (Finalization) и Корректировки (Adjust) для присваивания, которые вы можете заместить нужными вам программами. За деталями нужно обратиться к пакету Ada. Finalization, описанному в разделе 7.6 справочного руководства по языку Ada.
Class-wide-объекты
Память распределяется для каждого экземпляра класса:
C++ |
chars[100];
};
С с1,с2; //по 100 символов для с1 и с2
Иногда полезно иметь переменную, которая является общей для всех экземпляров класса. Например, чтобы присвоить порядковый номер каждому экземпляру, можно было бы завести переменную last для записи последнего присвоенного номера. В языке Ada это явно делается с помощью включения обычного объявления переменной в теле пакета:
package body P is
Last: Integer := 0;
Ada |
в то время как в языке'C++ нужно воспользоваться другим синтаксисом:
class С {
C++ |
chars[100];
};
int C::last = 0; // Определение, доступное за пределами файла
Спецификатор static в данном случае означает, что будет заведен один CW-объект*. Вы должны явно определить компонент static за пределами определения класса. Обратите внимание, что статический (static) компонент класса имеет внешнее связывание и может быть доступен из других файлов, в отличие от статического объявления в области файла.
Преобразование вверх и вниз
В разделе 14.4 мы описали, как в языке C++ значение порожденного класса может быть неявно преобразовано в значение базового класса. Это называется преобразованием вверх (up-conversion), потому что преобразование делается вверх от потомка к любому из его предков. Это также называется сужением (narrowing), Потому что производный тип «широкий» (так как он имеет дополнительные поля), в то время как базовый тип «узкий», он имеет только поля, которые являются общими для всех типов в производном семействе. Запомните, что преобразование вверх происходит только, когда значение производного типа непосредственно присваивается переменной базового типа, а не когда указатель присваивается от одной переменной другой.
Преобразование вниз (down-conversion) от значения базового типа к значению производного типа не допускается, поскольку мы не знаем, какие значения включить в дополнительные поля. Рассмотрим, однако, указатель на базовый тип:
Base_Class* Base_Ptr = new Base_Class;
C++ |
if (...) Base_Ptr = Derived_Ptr;
Derived_Ptr = Base_Ptr; // На какой тип указывает Base_Ptr?
Конечно, возможно, что Base_Ptr фактически укажет на объект производного типа; в этом случае нет никакой причины отклонить присваивание. С другой стороны, если указуемый объект фактически имеет базовый тип, мы делаем попытку преобразования вниз, и присваивание должно быть отвергнуто. Чтобы предусмотреть этот случай, в языке C++ определено динамическое преобразование типов (dynamic cast), которое является условным в зависимости от типа указуемого объекта:
C++ |
Если указуемый объект фактически имеет производный тип, преобразование завершается успешно. В противном случае указателю присваивается 0, и программист может это проверить.
Уже в языке Ada 83 допускалось явное преобразование между любыми двумя типами, порожденными друг из друга. Это не вызывало никаких проблем, потому что производные типы имеют в точности те же самые компоненты. Для них допустимо иметь различные представления (см. раздел 5.8), но преобразование типов совершенно четко определено, потому что оба представления имеют одинаковые число и типы компонентов.
Расширение преобразования производного типа до теговых типов не вызывает проблем в случае преобразования вверх от производного типа к базовому. Ненужные поля усекаются:
Ada |
A: Airplane_Data := Airplane_Data(S);
В другом направлении используются агрегаты расширения (extention aggregates), чтобы обеспечить значения для полей, которые были добавлены при расширении:
Ada |
Поля Speed и подобные берутся из соответствующих полей в значении А, а дополнительное поле Mach задано явно.
При попытке преобразования вниз CW-типа к конкретному типу делается проверка во время выполнения, и, если CW-объект не производного типа, произойдет исключительная ситуация:
Ada |
S:SST_Data;
begin
S := SST_Data(C); - Какой тип у С ??
exception
when Constraint_Error => .. .
end P;
- Глава 1
- 1.2. Процедурные языки
- 1.3. Языки, ориентированные на данные
- 1.4. Объектно-ориентированные языки
- 1.5. Непроцедурные языки
- 1.6. Стандартизация
- 1.7. Архитектура компьютера
- 1.8. Вычислимость
- 1.9. Упражнения
- Глава 2
- 2.2. Семантика
- 2.3. Данные
- 2.4. Оператор присваивания
- 2.5. Контроль соответствия типов
- 2.7. Подпрограммы
- 2.8. Модули
- 2.9. Упражнения
- Глава 3
- 3.1. Редактор
- 3.2. Компилятор
- 3.3. Библиотекарь
- 3.4. Компоновщик
- 3.5. Загрузчик
- 3.6. Отладчик
- 3.7. Профилировщик
- 3.8. Средства тестирования
- 3.9. Средства конфигурирования
- 3.10. Интерпретаторы
- 3.11. Упражнения
- Глава 4
- 4.1. Целочисленные типы
- I: Integer; -- Целое со знаком в языке Ada
- 4.2. Типы перечисления
- 4.3. Символьный тип
- 4.4. Булев тип
- 4.5. Подтипы
- 4.6. Производные типы
- 4.7. Выражения
- 4.8. Операторы присваивания
- 4.9. Упражнения
- Глава 5
- 5.1. Записи
- 5.2. Массивы
- 5.3. Массивы и контроль соответствия типов
- Подтипы массивов в языке Ada
- 5.5. Строковый тип
- 5.6. Многомерные массивы
- 5.7. Реализация массивов
- 5.8. Спецификация представления
- 5.9. Упражнения
- Глава 6
- 6.1. Операторы switch и case
- 6.2. Условные операторы
- 6.3. Операторы цикла
- 6.4. Цикл for
- 6.5. «Часовые»
- 6.6. Инварианты
- 6.7. Операторы goto
- 6.8. Упражнения
- Глава 7
- 7.1. Подпрограммы: процедуры и функции
- 7.2. Параметры
- 7.3. Передача параметров подпрограмме
- 7.4. Блочная структура
- 7.5. Рекурсия
- 7.6. Стековая архитектура
- 7.7. Еще о стековой архитектуре
- 7.8. Реализация на процессоре Intel 8086
- 7.9. Упражнения
- Глава 8
- 8.1 . Указательные типы
- 8.2. Структуры данных
- 8.3. Распределение памяти
- 8.4. Алгоритмы распределения динамической памяти
- 8.5. Упражнения
- Глава 9
- 9.1. Представление вещественных чисел
- 9.2. Языковая поддержка вещественных чисел
- 9.3. Три смертных греха
- Вещественные типы в языке Ada
- 9.5. Упражнения
- Глава 10
- 10.1. Преобразование типов
- 10.2. Перегрузка
- 10.3. Родовые (настраиваемые) сегменты
- 10.4. Вариантные записи
- 10.5. Динамическая диспетчеризация
- 10.6. Упражнения
- Глава 11
- 11.1. Требования обработки исключительных ситуаций
- 11.2. Исключения в pl/I
- 11.3. Исключения в Ada
- 11.5. Обработка ошибок в языке Eiffei
- 11.6. Упражнения
- Глава 12
- 12.1. Что такое параллелизм?
- 12.2. Общая память
- 12.3. Проблема взаимных исключений
- 12.4. Мониторы и защищенные переменные
- 12.5. Передача сообщений
- 12.6. Язык параллельного программирования оссаm
- 12.7. Рандеву в языке Ada
- 12.9. Упражнения
- Глава 13
- 13.1. Раздельная компиляция
- 13.2. Почему необходимы модули?
- 13.3. Пакеты в языке Ada
- 13.4. Абстрактные типы данных в языке Ada
- 13.6. Упражнения
- Глава 14
- 14.1. Объектно-ориентированное проектирование
- В каждом объекте должно скрываться одно важное проектное решение.
- 14.3. Наследование
- 14.5. Объектно-ориентированное программирование на языке Ada 95
- Динамический полиморфизм в языке Ada 95 имеет место, когда фактический параметр относится к cw-типу, а формальный параметр относится к конкретному типу.
- 14.6. Упражнения
- Глава 15
- 1. Структурированные классы.
- 15.1. Структурированные классы
- 5.2. Доступ к приватным компонентам
- 15.3. Данные класса
- 15.4. Язык программирования Eiffel
- Если свойство унаследовано от класса предка более чем одним путем, оно используется совместно; в противном случае свойства реплицируются.
- 15.5. Проектные соображения
- 15.6. Методы динамического полиморфизма
- 15.7. Упражнения
- 5Непроцедурные
- Глава 16
- 16.1. Почему именно функциональное программирование?
- 16.2. Функции
- 16.3. Составные типы
- 16.4. Функции более высокого порядка
- 16.5. Ленивые и жадные вычисления
- 16.6. Исключения
- 16.7. Среда
- 16.8. Упражнения
- Глава 17
- 17.2. Унификация
- 17.4. Более сложные понятия логического программирования
- 17.5. Упражнения
- Глава 18
- 18.1. Модель Java
- 18.2. Язык Java
- 18.3. Семантика ссылки
- 18.4. Полиморфные структуры данных
- 18.5. Инкапсуляция
- 18.6. Параллелизм
- 18.7. Библиотеки Java
- 8.8. Упражнения