
- •В.В. Чуркин технологии программирования
- •Содержание
- •Введение
- •Указатели. Операции над указателями Операции адресации и разыменования.
- •Арифметические операции.
- •Присваивание указателей.
- •Смещение и индексирование указателей.
- •Функции
- •Объявление функции (прототип)
- •Параметры функции
- •Встроенные функции
- •Функции с параметрами со значениями по умолчанию
- •Параметры функции main()
- •Рекурсивные функции
- •Перегрузка функций
- •Шаблоны функций
- •Указатели на функции
- •Объявление и инициализация массива указателей на функции:
- •Указатель на функцию как возвращаемое функцией значение
- •Выделение и освобождение динамической памяти
- •Символьные и строковые данные Символьные константы
- •Строковые константы (строки)
- •Символьные переменные
- •Строки – переменные
- •Специальные функции ввода-вывода строк
- •Стандартная библиотека функций языка с
- •Файлы Потоковый ввод-вывод в языке с Функции верхнего уровня файлового ввода-вывода
- •Открытие и закрытие потока
- •Текстовый режим
- •Бинарный режим
- •Закрытие файла
- •Функции в языке c для работы с файлами
- •Двоичный (бинарный) режим обмена с файлами
- •Строковый обмен с файлами
- •Форматный обмен с файлами
- •Позиционирование в потоке
- •Ввод-вывод нижнего уровня
- •Открытие / закрытие файла
- •Чтение и запись данных
- •Произвольный доступ к файлу
- •Позиционирование файлов
- •Сортировки числовых массивов Принцип наименьших привилегий
- •Обменная сортировка (SwapSort)
- •Сортировка выбором (SelectSort)
- •Пузырьковая сортировка (BubbleSort)
- •Сортировка вставками (InsertSort)
- •Быстрая сортировка (QuickSort)
- •Поиск в числовых массивах
- •Структуры
- •Форматы определения структурных типов
- •Форматы определения объектов структурных типов
- •Операции над объектами структурного типа
- •Доступ к элементам объектов структурного типа
- •Структуры, массивы и указатели
- •Объединения (смеси)
- •Оператор switch (переключатель)
- •Динамические структуры данных
- •Реализация стека с помощью массива
- •Очередь
- •Очередь приоритетов
- •Реализация очереди с помощью массива
- •Линейные списки
- •Функции для работы с двунаправленным линейным списком
- •Реализация списка с помощью массивов
- •Поиск хэшированием
- •Бинарные деревья
- •Бинарное упорядоченное дерево (дерево поиска)
- •Идеально сбалансированное дерево
- •Операции с бинарным упорядоченным деревом
- •Удаление узла из дерева
- •Обход (просмотр) дерева
- •Реализация дерева с помощью массивов
- •Вывод динамических структур в файл и чтение их из файла
- •Сбалансированные (avl) деревья
- •Алгоритм avl-вставки.
- •Повороты
- •Классы и объектно-ориентированное программирование
- •Объявление класса
- •Определение класса (реализация класса)
- •Использование класса (драйвер класса)
- •Доступ к элементам класса
- •Отделение интерфейса от реализации
- •Обслуживающие функции-утилиты
- •Конструкторы
- •Windows-программы в Builder
- •Структура головного файла проекта
- •Структура заголовочного файла модуля формы (“Unit1.H”)
- •Структура файла реализации модуля формы (“Unit1.Cpp”)
- •Области видимости (или области действия) переменных в блоках. Время жизни переменных
- •Доступ к свойствам и функциям-элементам (методам) объектов, переменным и функциям в приложении, содержащем одну форму
- •Константные объекты и константные функции-элементы
- •Перегрузка операций
- •Перегрузка унарных операций
- •Перегрузка бинарных операций
- •Перегрузка операции присваивания
- •Перегрузка операции приведения типа
- •Перегрузка операции индексирования []
- •Композиция классов
- •Дружественные функции класса
- •Дружественный класс
- •Использование указателя this
- •Статические элементы класса
- •Шаблон класса для статически и динамически создаваемых объектов
- •Конструктор 1
- •Деструктор
- •Вызовы конструкторов и деструкторов
- •Перегруженная операция присваивания
- •Конструктор 2 (конструктор копирования, конструктор копии)
- •Наследование. Иерархия классов.
- •Ключи доступа
- •Пример простого наследования (точка, круг)
- •Правила наследования функций-элементов. Вызовы конструкторов и деструкторов в иерархии
- •Виртуальные функции и полиморфизм
- •Правила определения и наследования виртуальных функций
- •Позднее (динамическое) связывание
- •Полиморфизм. Абстрактные и конкретные классы
- •Учебная литература (основная)
- •Учебная литература (для углубленного изучения)
- •Учебно-методические издания
Перегруженная операция присваивания
Создадим объекты A и B.
Оператор присваивания B=A скопирует в B статические данные объекта A.
Так как указателю mem2 в B присваивается значение указателя mem2 в A, то оба указателя будут ссылаться на один и тот же участок памяти. Но динамическую память, выделенную для B, после присваивания ссылки не будет.Предположим, оператор присваивания находится в функции:
void F()
{ dyn<int> A(2,3), B(4,5);
------------------------------
B=A;
}
По возвращении из функции F все объекты, созданные в функции F, уничтожаются вызовами деструктора, освобождающего динамическую память, на которую указывает mem2. Предположим, объект B уничтожается первым. Деструктор освобождает память, на которую указывает B.mem2 (на неё же указывает и A.mem2). При уничтожении A вызывается его деструктор для освобождения памяти по указателю A.mem2. Но этот блок памяти уже освобожден при уничтожении B, поэтому использование операции delete для деструктора теперь является ошибкой.
Правильное присваивание B=A должно выполняться так (действия 1 – копировать, 2 - копировать):
Следовательно, для правильного присваивания нужно перегрузить операцию присваивания (=) включением соответствующей функции-элемента в класс.
//----------------------------------------------------------------------------------------------
template <class T>
dyn<T>& dyn<T>::operator=(const dyn<T>& r)
{ mem1=r.mem1; // 1 - копирование статического элемента объекта r
//в статический элемент текущего объекта
*mem2=*r.mem2; // 2 - копирование динамического элемента объекта r
//в динамический элемент текущего объекта
cout<<”Оператор присваивания: “<<mem1<<’|’<<*mem2<<endl;
return *this;
}
//----------------------------------------------------------------------------------------------
Благодаря тому, что параметр r передается по константной ссылке, в этот параметр копируется только адрес объекта и исключаются любые изменения объекта.
Поскольку операция (=) возвращает ссылку на текущий объект, то можно связывать операторы присваивания: C=B=A;
Конструктор 2 (конструктор копирования, конструктор копии)
Конструктор копии создает новый объект, который должен быть копией другого, уже существующего объекта.
Наличие конструктора копии в классе обязательно, если объект имеет динамические данные. Приведем примеры, когда должен вызываться конструктор копии.
1)dyn<int> A(3,5), B=A; //инициализация создаваемого объекта B
//данными имеющегося объекта A
2)Инициализация создаваемого локального объекта данными существующего объекта имеет место при передаче объекта в функции в качестве параметра по значению, и при возвращении копии объекта из функции в качестве возвращаемого значения.
dyn<int> f(dyn<int> x)
{ dyn<int> obj;
--------------
return obj;
}
-----------------------
dyn<int> A(3,5), B(0,0); //объявления объектов
-------------------
B=f(A); //вызов f копированием A в x, x – создаваемый локальный объект
При выполнении возврата из f(A) создается копия obj, вызываются деструкторы для локальных объектов x и obj, а копия obj возвращается как значение функции.
Схема выполнения конструктора копии:
1 – копировать;
2 – выделить память под *mem2;
3 – копировать.
//----------------------------------------------------------------------------------------------
template <class T>
dyn<T>::dyn(const dyn<T>& x)
{ mem1=x.mem1;
mem2=new T(*x.mem2);
cout <<”Конструктор копии: ”<<mem1<<’|’<<*mem2<<endl;
}
//----------------------------------------------------------------------------------------------
Примечания.
1) Конструктор копии используется только тогда, когда создается объект.
2) Параметр в конструкторе копии должен передаваться по ссылке.
Предположим, в конструкторе копии параметр передается по значению:
dyn(dyn<T> x),
т.е. объект A в конструкторе копии передается параметру x по значению. Это означает, что создается локальный объект x – копия A. Для этого вызывается конструктор копии. Этот вызов, в свою очередь, требует вызова конструктора копии, и получается бесконечная цепь вызовов конструктора копии.
Вызовы конструкторов и деструкторов выполняются автоматически. В общем случае вызовы конструкторов выполняются в порядке, обратном вызовам конструкторов. Однако локальные статические объекты разрушаются по выходу из основной функции, в отличие от локальных автоматических, которые разрушаются по выходу из вызываемой функции.