
- •1. Введение
- •2. Элементы языка
- •2.1. Первые программы
- •2.2. Алфавит языка
- •2.3. Комментарии
- •2.4. Типы данных
- •2.5. Целые типы данных
- •2.6. Плавающие типы данных
- •2.7. Константы-строки, или литералы
- •2.8. Директива препроцессора define
- •2.9. Описания
- •2.10. Модификатор const
- •3. Выражения
- •3.1. Операция и выражение присваивания
- •3.2. Арифметические операции
- •3.3. Операции отношения
- •3.4. Логические операции
- •3.5. Побитовые операции
- •3.6. Сдвиги
- •?????3.8. Тернарная или условная операция
- •3.9. Операция следования
- •3.10. Приоритеты операций и порядок вычисления
- •4.6. Оператор выбора switch
- •4.7. Оператор цикла while
- •4.8. Цикл с постусловием do-while
- •4.9. Оператор for
- •4.10. Оператор безусловного перехода
- •4.11. Оператор break
- •4.12. Оператор continue
- •4.13. Оператор return
- •5. Указатели
- •5.1. Определение указателей
- •5.2. Указатели и массивы
- •5.3. Адресная арифметика
- •5.4. Символьные массивы и строки
- •5.5. Многомерные массивы
- •5.6. Указатели и многомерные массивы
- •6. Операция sizeof
- •7. Операции для работы с динамической памятью
- •7.1. Операция выделения памяти new
- •7.2. Операция освобождения памяти delete
- •8. Функции
- •8.1. Определение и вызов функции
- •8.2. Функции. Передача аргументов
- •8.3. Передача многомерных массивов
- •8.4. Указатели на функции
- •8.5. Ссылки
- •8.6. Ссылки в качестве параметров функций
- •8.7. Аргументы по умолчанию
- •8.8. Переопределение функций
- •8.9. Шаблоны функций
- •9. Объявления и определения
- •10. Область существования имени
- •11. Область видимости
- •Здесь будет напечатано
- •12. Классы памяти
- •13. Объявления объектов и типов
- •14. Имена типов
- •15. Синоним имени типа
- •16. Правила преобразования стандартных типов
- •16.1. Явные преобразования
- •16.2. Неявные преобразования стандартных базовых типов
- •16.3. Преобразование производных стандартных типов
- •17. Перечисления
- •18. Классы
- •18.1. Объявление классов
- •18.2. Конструкторы
- •18.3. Деструкторы
- •18.4. Статические члены класса
- •18.5. Указатель this
- •18.6. Статические функции-члены
- •18.7. Указатели на члены класса
- •18.8. Инициализация данных–членов класса
- •18.9. Конструктор копирования и операция присваивания
- •18.10. Дружественные функции
- •18.11. Конструктор и операция new
- •18.12. Вызов деструктора
- •19. Производные классы
- •19.1. Построение производного класса
- •19.2. Защищенные члены класса
- •19.3. Управление уровнем доступа к членам класса
- •19.4. Последовательность вызова конструктора и деструктора при построении производного класса на основе одного базового
- •19.5. Преобразования типов
- •20. Полиморфизм
- •20.1. Раннее и позднее связывание
- •20.2. Виртуальные функции
- •20.3. Абстрактные классы
- •21. Переопределение стандартных операций
- •21.1. Основные определения и свойства
- •21.2. Операции new и delete при работе с абстрактными типами
- •21.3. Использование new при создании динамического объекта абстрактного типа
- •21.4. Операция delete
- •21.5. Преобразование типов
- •22. Некоторые особенности переопределенных операций
- •22.2. Операция [ ]
- •23. Классы и шаблоны
- •24. Списки
- •24.1. Операции над односвязными списками
- •24.2. Двунаправленные и кольцевые списки
- •24.3. Операции над кольцевыми списками
- •25. Стеки
- •25.1. Реализация стека через массив
- •25.2. Реализация стека через динамическую цепочку звеньев
- •26. Двоичные деревья
- •26.1. Определение и построение
- •26.2.Таблицы
- •27. Список литературы
19.4. Последовательность вызова конструктора и деструктора при построении производного класса на основе одного базового
Объект производного класса в качестве данных-членов класса, может содержать объекты абстрактных типов.
class string{. . .
public:
string (char*);
~string ();
…
};
class Base{…
public:
Base (int);
~Base ();
. . .
};
class Derived: public Base {
Base b;
string s;
public:
Derived (char*, int);
~Derived ();
. . .
};
Перед обращением к собственно конструктору класса Derived необходимо, во-первых, создать подобъект типа Base, во- вторых, члены b и s. Поскольку для их создания нужно обратиться к конструкторам соответствующих классов, мы должны им всем передать необходимые списки аргументов:
Derived::Derived (char *st, int len): Base (len), b (len+1), s (str) {…}
В этом случае при создании объекта типа Derived сначала будет создан подобъект типа Base. При этом будет вызван конструктор Base::Base() с аргументом len. Затем будут созданы объекты b и s в том порядке, в котором они указаны в определении класса Derived. После этого будет выполнен конструктор Derived::Derived (). Деструкторы будут вызваны в обратном порядке.
19.5. Преобразования типов
Объект производного типа может рассматриваться как объект его базового тип. Обратное неверно. (Кошка есть млекопитающее, но не любое млекопитающее есть кошка.) Компилятор может неявно выполнить преобразование объекта производного типа к объекту типа базового:
class Base {…};
class Der: public Base {…};
Der derived;
Base b = derived;
Обратное преобразование – Base к Der – должно быть определено программистом:
Der tmp = b; // Ошибка, если для Der
// не определён конструктор Der (Base).
Значительно чаще, чем преобразование типов, используется преобразование указателей, или ссылок на эти типы. Существует два типа преобразования указателей – явное и неявное. Явное преобразование будет выполнено всегда, неявное – только в определённых случаях.
Если базовый класс является общим (public) базовым классом, т.е. мы имеем дело с отношением вида
class Base {…};
class Der: public Base {…};
то принципы преобразования очень просты: неявно может быть выполнено преобразование указателя типа Der* к указателю типа Base*. Обратное преобразование обязательно должно быть явным.
Другими словам, при обращении через указатели объект производного типа может рассматриваться как объект базового тип. Обратное утверждение неверно:
Der derived;
Base *bp = &derived; // Неявное преобразование.
Der *dp1 = bp; // Ошибка.
Der *dp2 = (Der*) bp; // Явное преобразование; теперь верно.
То, что производный класс в некотором смысле может рассматриваться как его базовый, оказывает влияние на выбор нужной версии переопределённой функции. Сложность возникает, если для выбора одного из вариантов нужно выполнить неявное преобразование типов.
Правила здесь таковы.
Если нет точного соответствия списка формальных и фактических параметров, то наивысший приоритет среди выполняемых преобразований имеют преобразования производного типа к базовому. Это относится как к самому типу, так и к указателю на него. Только в том случае, если это невозможно, компилятор пробует выполнить другие преобразования (например, стандартные преобразования указателей).
Пример:
class Base {…};
class Der: public Base {…};
func (Base*);
func (void*);
…
Der *dp;
float *fp;
func (dp); // Вызов func (Base*).
func (fp); // Вызов func (void*).
Если в программе используются несколько уровней производных классов, то при выполнении неявных преобразований типа указателя ищется класс “ближайшего” уровня:
class Base {…};
class A: public Base {…};
class B: public A{…};
func (Base*);
func (A*);
…
B *db;
func (db); // Вызов функции func (A*).