С++ и Объектно- Ориентированное Программирование

Курс лекций

Язык С++ в настоящее время становится одним из основных языков воплощения идей ООП для практической разработки как системных, так и прикладных программ.
Данный специальный курс посвящен практическому освоению основных идей ООП на основе алгоритмического языка C++ в редакции Borland C++ 2.0.
Проводится концептуальный анализ средств ООП. Рассматриваются такие понятия как инкапсуляция, наследование, полиморфизм. Все положения иллюстрируются конкретными наглядными примерами, которые студенты реализуют в виде действующих программ на лабораторных занятиях.
Ввиду ограниченности времени в данном курсе не ставится цель овладеть всеми возможностями алгоритмического языка С++ (из рассмотрения были исключены такие понятия как расширенные возможности ввода-вывода в потоки, библиотеки классов и др. конкретные особенности языка). При этом четкого и последовательного изложения синтаксиса языка С++ вообще не производится, полагаясь на устойчивые практические знания студентов языка Turbo C, полученные в курсе ВМП.
Программа данного спецкурса предполагает (кроме первых двух лекций) чередование лекционных и практических (лабораторных) занятий. Для практических занятий необходим класс компьютеров типа IBM PC с установленным компилятором Borland C++ версии 2.0 или выше под управлением операционной системы MS DOS (как минимум) и возможностью работы каждого студента на отдельном компьютере.
Общее количество часов (трудоемкость) - 67 час. в том числе: лекции - 20 час.; практические занятия - 18 час.
Отчетности: зачет, (экзамен по желанию).

Телефон: (343)350-24-73
Факс: (343)350-24-73
Эл.почта: detc@usu.ru

620083 г. Екатеринбург
Пр. Ленина, 51
Уральский государственный университет
Им. А.М. Горького


No Slide Title

На этой странице используется разбиение на окна, не поддерживаемое данным обозревателем.


С++ и Объектно-ориентированное Программирование

На этой странице используется разбиение на окна, не поддерживаемое данным обозревателем.


Заголовок слайда отсутствует

На этой странице используется разбиение на окна, не поддерживаемое данным обозревателем.


Данная презентация содержит объекты, не отображаемые используемым обозревателем. Она рассчитана на просмотр с помощью более поздних версий Microsoft Internet Explorer.

Если вы все же хотите продолжить, щелкните здесь.


‹верхний колонтитул›
‹дата/время›
Click to edit Master text styles
Second level
Third level
Fourth level
Fifth level
‹нижний колонтитул›
‹#›


‹верхний колонтитул›
‹дата/время›
‹нижний колонтитул›
‹#›


Click to edit Master title style
Click to edit Master text styles
Second level
Third level
Fourth level
Fifth level
‹дата/время›
‹нижний колонтитул›
‹#›


Заметки
Структура
С++ и Объектно-ориентированное
Программирование
План курса
Литература
1. Рассохин Д. От Си к Си++, издательство "ЭДЭЛЬ", Москва, 1993.
2. Ирэ Пол Объектно-ориентированное программирование с использованием С++, Киев "ДиаСофт", 1995.
3. Тимоти Бадд Объектно-ориентированное программирование в действии, издательство “Питер”, 1997.
4. Майкл Хаймен Borland C++ для «Чайников», издательство «Диалектика», Киев, 1996.
5. Бьерн Страуструп Язык программирования С++, 1999г, 3-е издание.
”Введение в ООП“
Слайд 5
Эволюция парадигм программирования
Три принципа ООП
Инкапсуляция
Инкапсуляция
Наследование
Полиморфизм
Преимущества использования ООП
Модульность, защита и надежность
Повторное использование кода
Гибкость при развитии и изменении
Средство реализации больших проектов
Термины


No Slide Title

”Введение в ООП“
ООП - новая технология программирования, основанная на моделировании реального мира, при котором детали его реализации скрыты.
ООП - взгляд на программирование, основанный на данных, в котором данные и поведение жестко связаны.
ОПП-методология программирования, основанная на представлении программы в виде совокупности объектов, каждый из которых является экземпляром определенного класса, а классы образуют иерархию наследования.
(Simula 67, Smalltalk, Lisp, C++, Object Pascal, Java,
Java Script, Perl, Pyton, Visual FoxPro, Oracle…)


No Slide Title

1.Введение в ООП. Основные принципы
2.Инкапсуляция. Описание Класса.
3.Наследование. Производные Классы.
4.Множественное наследование.
5.Полиморфизм. Виртуальные функции.
6.Особенности С++, не связанные с ООП
7.Ссылки и динамические объекты.
8.Перегрузка операций и дружественные функции.
9.Шаблоны функций и шаблоны классов.
10.Обработка исключительных ситуаций.
11.Основы проектирования.
12.Моделирование объектов реального мира посредством классов.
План курса


No Slide Title

Инкапсуляция
Объединение данных с функциями, предназначен-ными для манипулирования этими данными (т.е. поведением) в новом типе - классе.
Объекты - экземпляры класса.
Данные
Код
Традиционный подход
Данные
Методы
Класс
Объектно-ориентированный подход


No Slide Title

Наследование
- Механизм, позволяющий объектам класса наследовать характеристики (данные и методы ) более простых и общих типов (классов). - средство получения новых классов из существующих
Домашние животные


No Slide Title

Полиморфизм
Многоформенность - в С++ механизм, позволяющий использовать одинаковые Имена, для сходных по смыслу действий и методов, относящихся к различным объектам (типам и классам).
Свойства :
•Перегрузка функций
•Перекрытие методов
•Перегрузка операций
•Виртуальные функции и позднее связывание
•Семейства и шаблоны классов


No Slide Title

Преимущества использования ООП
•Модульность, защита и надежность
•Повторное использование кода
•
•Гибкость при развитии и изменении
•Средство реализации больших проектов


No Slide Title

Три принципа ООП
Инкапсуляция
Наследование
Полиморфизм


No Slide Title

Термины
h ООП
h Си++
h Инкапсуляция
h Наследование
h Полиморфизм
h Класс
h Объект
h Метод
h


No Slide Title

Литература
•1. Рассохин Д. От Си к Си++, издательство "ЭДЭЛЬ", Москва, 1993.
•2. Ирэ Пол Объектно-ориентированное программирование с использованием С++, Киев "ДиаСофт", 1995.
•3. Тимоти Бадд Объектно-ориентированное программирование в действии, издательство “Питер”, 1997.
•4. Майкл Хаймен Borland C++ для «Чайников», издательство «Диалектика», Киев, 1996.
•5. Бьерн Страуструп Язык программирования С++, 1999г, 3-е издание.
•


No Slide Title

С++ и Объектно-ориентированное
Программирование
Уральский государственный университет
Кафедра Математической Физики,
Центр технологий дистанционного образования
Курс Лекций
Устинов В.А., Кискачи А.В.


No Slide Title

Инкапсуляция
Объекты обладают свойством скрытия информации, т.е. им обычно не позволено знать, как реализуются другие объекты - детали реализации спрятаны внутри самих объектов. Несомненно, можно ездить на автомобиле, не зная технических деталей его внутреннего функционирования - трансмиссии, выхлопной трубы и др.


No Slide Title

• ООП - моделирование деятельности мира
• Программирование для Windows и Internet
• Работа с современными базами данных
• Разработка больших проектов
Язык программирования является объектно-ориентированным тогда и только тогда, когда выполняются следующие условия:
4Поддерживаются объекты, т.е. абстракции данных, имеющие интерфейс в виде именованных операций и собственные данные, с ограничением доступа к ним.
4Объекты относятся к соответствующим типам (классам).
4Типы (классы) могут наследовать атрибуты базовых классов.
”Введение в ООП“


No Slide Title

Эволюция парадигм программирования
Процедурное программирование
Реши, какие требуются процедуры; используй наилучшие доступные алгоритмы.
Модульное программирование
Реши, какие требуются модули; разбей программу так, чтобы скрыть данные в модулях.
Объектно-ориентированное программирование
Реши, какие требуются классы; обеспечь полный набор операций для каждого класса; явно вырази общность через наследование.
Обобщенное программирование
Реши, какие требуются алгоритмы;параметризуй их так, чтобы они могли работать со множеством подходящих типов и структур данных.


‹верхний колонтитул›
‹дата/время›
Click to edit Master text styles
Second level
Third level
Fourth level
Fifth level
‹нижний колонтитул›
‹#›
Расширяемость C++ может приводить к увеличению количества и видов возможных ошибок. Каждый новый класс добавляет свои собственные возможные ошибки. Возможности языка, описываемые ниже, позволяют программистам писать более ясные, более живучие, более отказоустойчивые программы. Обработка ошибок изменяется качественно и количественно в зависимости от того, предназначена ли данная прикладная программа для распространения, или нет. Изделия, предназначенные для распространения, как правило, предусматривают более тщательную обработку ошибок, чем временный программный продукт. Существует много способов борьбы с ошибками. Часто обработка распределяется по всему тексту программы, где они могут появляться. Преимущество такого подхода в том, что читающий программу может видеть обработку ошибки в непосредственной близости от соответствующих операторов. Однако основной код при этом становится более трудным для восприятия и поддержки.
Вот некоторые распространенные примеры исключений: нехватка памяти,
выход индекса за пределы массива, арифметическое переполнение, деление на нуль и недопустимые параметры функций… Механизм обработки исключений позволяет выносить код обработки ошибок из основной линии программы. Это облегчает чтение программы и внесение в нее изменений, а также не противоречит достоинствам блочного построения. Другое преимущество нового стиля обработки исключений - возможность перехватывать или все виды исключений, или только исключения некоторого определенного типа, или исключения взаимосвязанных типов.
1.“Исключение” – слово, обозначающее для разных людей разноеJ.
2.Обработка исключений используется в случаях, при которых система может быть восстановлена для нормальной работы после ошибки, вызвавшей исключение. Эта процедура называется обработчиком исключения.
Рекомендуется:
a) использовать обычные методы обработки ошибок (а не обработку исключений) для построения непосредственного, локального обработчика ошибок, в котором программе легче управляться со своими собственными ошибками.
b) когда идет работа с библиотеками, источник вызова библиотечной функции будет, вероятно, использовать свой уникальный обработчик исключения , сгенерированного библиотечной функцией. Маловероятно, чтобы библиотечная функция выполняла такую обработку ошибки, которая бы удовлетворила конкретные потребности всех пользователей. Следовательно, исключения – удобный способ работы с ошибками, возникающими в библиотечных функциях…
1)void assert(int выражение);
Если проверяемое этой утилитой выражение ложно, то в стандартный файл ошибок в формате, определяемом системой программирования, выдается диагностическое сообщение об ошибке. Программа завершается и данный фрагмент программы должен быть исправлен программистом. Это полезно во время отладки. (assert в конце вызывает функцию abort).
2) Недопустимо в программах, предназначенных для широкого распространения, а также в целевом программном обеспечении, необходимом для работы в критических ситуациях. Однако для программ, создаваемых для решения ваших частных задач совершенно нормально игнорировать многие виды ошибок…
3) Это не дает программе закончиться нормально и при этом выдать неправильные результаты. Фактически, для многих типов ошибок прерывание – хорошая стратегия, особенно для исправимых ошибок, которые дают программе доработать до конца и вводят в заблуждение программиста, считающего, что программа функционировала правильно. Для ответственных приложений это также не подходит.
4)errno из <errno.h> - величина типа int, которой некоторые библиотечные функции могут присваивать положительный код ошибки. Однако errno может получить ненулевое значение в результате вызова б.ф. независимо от того , произошла ошибка или нет, потому как ее использование не документировано в стандарте описания функций.
6)setjump и longjump позволяют перейти к обработчику ошибки , минуя глубоко вложенные вызовы функций. Опасны тем, что разматывают текст без вызовов деструкторов автоматических объектов.
Исключения генерируются только внутри блока try. Генерация исключения вне блока вызывает обращение к прерыванию программы.
Для генерации или возбуждения исключения используется ключевое слово throw , определяющее тип объекта исключения или генерирующее условное выражение.
После генерации исключение перехватывается ближайшим обработчиком исключений catch (к блоку try), содержащим спецификацию соответствующего типа. Выполняется первый обработчик соответствующего типа. После этого управление передается на первый оператор после последнего блока catch.
Пустой оператор throw повторно генерирует то же самое исключение. Если никакое исключение не было сгенерировано, то оператор повторной генерации вызывает обращение к функции завершения terminate. Таким образом оператор throw должен появляться только в обработчике catch. Даже если обработчик может обработать исключение и независимо от того, делает ли он какую-либо обработку этого исключения, он может повторно возбудить исключение для последующей обработки его вне этого обработчика. Повторно сгенерированное исключение обнаруживается следующим внешним блоком try и перехватывается обработчиком из списка, следующего за этим внешним блоком try.
Функция может генерировать данные исключения, либо типы производные от них.
Во втором случае исключение не должно покинуть сам деструктор. Если все-таки покинет, это считается ошибкой механизма обработки исключений и вызывает std::terminate(). Все же не существует общего способа определить , в праве ли механизм обработки исключений или деструктор проигнорировать одно исключение ради обработки другого.


Заметки
Структура
Лекция № 10
Обработка исключительных ситуаций
Цели
Понять, что такое исключения и как они обрабатываются.
Научиться использовать блоки try для обработки, которая должна выполняться при возникновении исключения.
Научиться генерировать исключения в точке их возникновения.
Научиться использовать блоки catch для описания обработчиков исключений.
Понять, как обрабатываются не перехваченные и непредусмотренные исключения.
Обработка исключений должна использоваться:
Обработка исключений не предназначена для работы с асинхронными ситуациями типа
Другие методы обработки ошибок.
Генерация и перехват исключений
Основы обработки исключений
Слайд 8
Как фиксируется соответствие типа?
Типичные ошибки программирования
Обработчиков исключений может:
Слайд 12
Спецификация исключений
Обработка непредусмотренных исключений
Как сообщить об ошибке в конструкторе?
Слайд 16
Исключения в деструкторах.
Слайд 18
Термины


No Slide Title

Термины
•генерация исключений
•объект исключения
•перехват исключений
•спецификация исключений


No Slide Title

Спецификация исключений
int g (float h)
throw(a,b,c)
{
// тело функции
}
Генерация непредусмотренных исключений вызывает функцию unexpected
Спецификация исключений
Это
список исключений, которые может генерировать данная функция.
void g() любое исключение
void g() throw() никакое
Функция может генерировать данные исключения, либо типы производные от них.


No Slide Title

Обработка исключений должна использоваться:
•для обработки только исключительных ситуаций;
•для обработки исключений, возникающих в тех компонентах программы, которые сами не имеют механизма обработки этих исключений;
•для обработки исключений, возникающих в таких компонентах программы, как функции, библиотеки и классы, которые широко используются и в которых не имеет смысла вводить свою собственную обработку исключений;
•в больших проектах, чтобы обрабатывать одинаковым способом ошибки, возникающие в различных местах проекта.
*
1.“Исключение” – слово, обозначающее для разных людей разноеJ.
2.Обработка исключений используется в случаях, при которых система может быть восстановлена для нормальной работы после ошибки, вызвавшей исключение. Эта процедура называется обработчиком исключения.
Рекомендуется:
a) использовать обычные методы обработки ошибок (а не обработку исключений) для построения непосредственного, локального обработчика ошибок, в котором программе легче управляться со своими собственными ошибками.
b) когда идет работа с библиотеками, источник вызова библиотечной функции будет, вероятно, использовать свой уникальный обработчик исключения , сгенерированного библиотечной функцией. Маловероятно, чтобы библиотечная функция выполняла такую обработку ошибки, которая бы удовлетворила конкретные потребности всех пользователей. Следовательно, исключения – удобный способ работы с ошибками, возникающими в библиотечных функциях…


No Slide Title

Основы обработки исключений
// Простой пример обработки исключения.
// Контроль ошибки деления на ноль.
#include <iostream.h>
class DevideByZeroError {
public:
DevideByZeroError(){message="Division by zero"; }
void printMessage() const {cout<<message; }
private:
const char *message;
};
float quotient(int n1, int n2)
{ if (n2==0) { throw DevideByZeroError(); };
return (float) n1/n2;
}


No Slide Title

Другие методы обработки ошибок.
•Использование утилиты assert.
•Простое игнорирование исключений.
•Прерывание программы (‘утечка ресурсов’).
•Установка некоторого индикатора ошибки.(errno)
•Проверка условия ошибки, выдача сообщения об ошибке и вызов exit .
•setjump и longjump из <setjump.h> .
•Выделение некоторых специфических видов ошибки для их обработки. Например, когда new дает сбой при выделении памяти, она может заставить выполняться функцию new_handler для обработки этой ошибки.
•
*
1)void assert(int выражение);
Если проверяемое этой утилитой выражение ложно, то в стандартный файл ошибок в формате, определяемом системой программирования, выдается диагностическое сообщение об ошибке. Программа завершается и данный фрагмент программы должен быть исправлен программистом. Это полезно во время отладки. (assert в конце вызывает функцию abort).
2) Недопустимо в программах, предназначенных для широкого распространения, а также в целевом программном обеспечении, необходимом для работы в критических ситуациях. Однако для программ, создаваемых для решения ваших частных задач совершенно нормально игнорировать многие виды ошибок…
3) Это не дает программе закончиться нормально и при этом выдать неправильные результаты. Фактически, для многих типов ошибок прерывание – хорошая стратегия, особенно для исправимых ошибок, которые дают программе доработать до конца и вводят в заблуждение программиста, считающего, что программа функционировала правильно. Для ответственных приложений это также не подходит.
4)errno из <errno.h> - величина типа int, которой некоторые библиотечные функции могут присваивать положительный код ошибки. Однако errno может получить ненулевое значение в результате вызова б.ф. независимо от того , произошла ошибка или нет, потому как ее использование не документировано в стандарте описания функций.
6)setjump и longjump позволяют перейти к обработчику ошибки , минуя глубоко вложенные вызовы функций. Опасны тем, что разматывают текст без вызовов деструкторов автоматических объектов.


No Slide Title

main ()
{ cout<<"Two numbers, please :";
int nu1, nu2;
cin>>nu1>>nu2;
try {
float result=quotient(nu1,nu2);
cout<< "it will be" << result << endl;
}
catch (DevideByZeroError error) {
cout << "Error:";
error.printMessage();
cout << endl;
return 1; } // завершение при ошибке
return 0; // нормальное завершение
}


No Slide Title

Генерация и перехват исключений
…
try
{ …
… }
… throw операнд;
catch() {
… }
*
Исключения генерируются только внутри блока try. Генерация исключения вне блока вызывает обращение к прерыванию программы.
Для генерации или возбуждения исключения используется ключевое слово throw , определяющее тип объекта исключения или генерирующее условное выражение.
После генерации исключение перехватывается ближайшим обработчиком исключений catch (к блоку try), содержащим спецификацию соответствующего типа. Выполняется первый обработчик соответствующего типа. После этого управление передается на первый оператор после последнего блока catch.


No Slide Title

Как фиксируется соответствие типа?
•Тип параметра в заголовке и тип сгенерирован-ного объекта действительно одинаковые.
•Тип параметра обработчика catch является открытым базовым классом класса сгенерированного объекта.
•Параметр обработчика имеет тип указатель и сгенерированный объект имеет тип указатель, преобразуемый в тип параметра путем допустимых преобразований указателей. (упк->убк).
•Если после catch в круглых скобках записано многоточие: catch(…) это означает, что будут перехватываться все исключения. Такой блок размещается только последним в списке обработчиков объектов, иначе возникает синтаксическая ошибка.


No Slide Title

Цели
•Понять, что такое исключения и как они обрабатываются.
•Научиться использовать блоки try для обработки, которая должна выполняться при возникновении исключения.
•Научиться генерировать исключения в точке их возникновения.
•Научиться использовать блоки catch для описания обработчиков исключений.
•Понять, как обрабатываются не перехваченные и непредусмотренные исключения.
*
Расширяемость C++ может приводить к увеличению количества и видов возможных ошибок. Каждый новый класс добавляет свои собственные возможные ошибки. Возможности языка, описываемые ниже, позволяют программистам писать более ясные, более живучие, более отказоустойчивые программы.
Обработка ошибок изменяется качественно и количественно в зависимости от того, предназначена ли данная прикладная программа для распространения, или нет. Изделия, предназначенные для распространения, как правило, предусматривают более тщательную обработку ошибок, чем временный программный продукт.
Существует много способов борьбы с ошибками. Часто обработка распределяется по всему тексту программы, где они могут появляться. Преимущество такого подхода в том, что читающий программу может видеть обработку ошибки в непосредственной близости от соответствующих операторов. Однако основной код при этом становится более трудным для восприятия и поддержки.
Вот некоторые распространенные примеры исключений: нехватка памяти,
выход индекса за пределы массива, арифметическое переполнение, деление на нуль и недопустимые параметры функций…
Механизм обработки исключений позволяет выносить код обработки ошибок из основной линии программы. Это облегчает чтение программы и внесение в нее изменений, а также не противоречит достоинствам блочного построения.
Другое преимущество нового стиля обработки исключений - возможность перехватывать или все виды исключений, или только исключения некоторого определенного типа, или исключения взаимосвязанных типов.


No Slide Title

Типичные ошибки программирования
•Порядок перехвата обеспечен разработчиком неверно.
•Размещение обработчика исключения с типом аргумента void* перед обработчиками исключений с другими типами указателей вызывает синтаксическую ошибку (перехватываются все исключения типа указатель).


No Slide Title

•Предположение, что исключение, сгенерированное обработчиком catch, будет обработано этим или любым другим обработчиком, связанным с тем же блоком try, который сгенерировал первоначальное исключение.
•Размещение пустого оператора throw вне обработчика catch ; выполнение такого оператора приведет к вызову функции terminate.
Типичные ошибки программирования
Пустой оператор throw повторно генерирует то же самое исключение. Если никакое исключение не было сгенерировано, то оператор повторной генерации вызывает обращение к функции завершения terminate. Таким образом оператор throw должен появляться только в обработчике catch.
Даже если обработчик может обработать исключение и независимо от того, делает ли он какую-либо обработку этого исключения, он может повторно возбудить исключение для последующей обработки его вне этого обработчика.
Повторно сгенерированное исключение обнаруживается следующим внешним блоком try и перехватывается обработчиком из списка, следующего за этим внешним блоком try.


No Slide Title

Обработка непредусмотренных исключений
Функция terminate может быть вызвана различными путями:
•явно;
•если сгенерированное исключение не может быть перехвачено;
•как заданное по умолчанию действие при вызове unexpected;
•во время разматывания стека, вызванного исключением, и попытки со стороны деструктора сгенерировать исключение, что вызывает обращение к terminate.
unexpected, set_unexpected
terminate, set_terminate,
abort


No Slide Title

Обработка исключений не предназначена для работы с асинхронными ситуациями типа
•комбинирование клавиш <ctrl> c в Unix или Dos;
•запрос операционной системы о завершении работы;
•поступление сообщений от сети;
•щелчок мыши...
*
•Библиотека обработки сигналов (прерываний) <signal.h> содержит функцию signal , перехватывающую непредвиденные события. Сигналы можно генерировать функцией raise .


No Slide Title

Обработчиков исключений может:
•рассмотреть ошибку и решить вызвать функцию terminate;
•повторно сгенерировать исключение;
•преобразовать один тип исключения в другой, генерируя это другое исключение;
•выполнить любые необходимые восстановления и продолжить выполнение после последнего обработчика исключения;
•рассмотреть ситуацию, вызвавшую ошибку, удалить причину ошибки и повторить вызов первоначальной функции, которая вызвала исключение;
•просто возвращать некоторое значение состояния в среду.
•


No Slide Title

Как сообщить об ошибке в конструкторе?
•Возвратить объект в неправильном состоянии и полагаться на то, что пользователь проверит его состояние.
•Присвоить значение нелокальной переменной (нпр, errno ) для указания на неуспешное состояние объекта и полагаться на то, что пользователь проверит ее состояние.
•Не осуществлять инициализации в конструкторе и полагаться на то, что пользователь вызовет функцию инициализации до первого использования.
Обработка исключений позволяет
передать информацию о неуспешной
инициализации из конструктора.


No Slide Title

// Простой класс Vector можно защитить от
// запроса слишком большого кол-ва памяти.
class Vector {
public:
class Size { };
int max=32000;
Vector (int sz)
{ if (sz<0||max<sz) throw Size(); … }
};
Vector * f(int i)
{ try {
Vector *p = new Vector(i);
… return p;}
catch( Vector::Size) {…}
}


No Slide Title

Исключения в деструкторах.
•Нормальный вызов: в результате нормального выхода имени из области видимости, использования оператора delete.
•Вызов в процессе обработки исключения: в процессе раскручивания стека механизм обработки исключения приводит к выходу из области видимости, содержащей объект с деструктором.
С точки зрения обработки исключений
деструктор может вызываться одним из двух
cпособов:
Во втором случае исключение не должно покинуть сам деструктор. Если все-таки покинет, это считается ошибкой механизма обработки исключений и вызывает std::terminate(). Все же не существует общего способа определить , в праве ли механизм обработки исключений или деструктор проигнорировать одно исключение ради обработки другого.


No Slide Title

Деструктор в состоянии защитить себя, если он вызывает функции, которые могут сгенерировать исключение.
X::~X() {
try { f(); // может генерировать исключение}
catch(…) {//некоторые действия}
}
Функция стандартной библиотеки uncaught_exception() возвращает true, если было сгенерировано исключение, которое еще не перехвачено. Она позволяет программисту определять различные действия в деструкторе в зависимости от того, уничтожен объект нормально или в процессе раскручивания стека.


No Slide Title

Лекция № 10
Обработка исключительных ситуаций
Уральский государственный университет
Кафедра Математической Физики,
Центр технологий дистанционного образования
Курс Лекций
С++ и Объектно-ориентированное
Программирование
Устинов В.А., Кискачи А.В.


‹верхний колонтитул›
‹дата/время›
Щелчок правит образец текста
Второй уровень
Третий уровень
Четвертый уровень
Пятый уровень
‹нижний колонтитул›
‹#›


Щелчок правит образец заголовка
Щелчок правит образец текста
Второй уровень
Третий уровень
Четвертый уровень
Пятый уровень
‹дата/время›
‹нижний колонтитул›
‹#›


Заметки
Структура

Лекция № 11
Стандартные контейнеры.
Понятие контейнера.
Контейнер-это объект, содержащий другие объекты. Примерами контейнеров являются списки и вектора. Как правило, в контейнер можно добавлять объекты и удалять их из него.
Последовательности.
Стандартная библиотека обеспечивает следующие фундаментальные последовательности:
вектора(vector)
списки(list)
очереди с двумя концами(deque).
Из них предоставлением соответствующего интерфейса, созданы
стеки(stack);
очереди(queue);
очереди с приоритетами(priority_queue).
Вектора.
Если не утверждается обратное, всё сказанное о векторе относится ко всем стандартным контейнерам.
Слайд 5
Конструкторы.
Слайд 7
Операции со стеком.
Функции push_back() и pop_back() обращаются с вектором как со стеком, добавляя элементы в конец и удаляя последние элементы.
Операции, характерные для списков.
Иногда полезно добавлять элементы в середину вектора или удалять их.
Рассмотрим эти операции на примере.
"Можно удалять элементы по одному"
Можно удалять элементы по одному.
fruit.erise(find(fruit.begin(),fruit.end(),”pear”));
"Мы можем также вставить целую..."
Мы можем также вставить целую последовательность.
Функции-помощники.
Два вектора можно сравнить при помощи операторов = = и <.
Два вектора v1 и v2 считаются равными, если v1.size()= =v2.size() и v1[n]= = v2[n] для любого допустимого индекса n.
Списки.
Список-это последовательность, оптимизированная для вставки и удаления элементов. По сравнению с вектором(и deque) обращение к элементу списка по индексу оказалось бы чрезвычайно медленным. Поэтому список обеспечивает двунаправленные итераторы, что приводит к тому, что список, как правило реализуется при помощи некоторой формы двусвязного списка.
Контейнер list обеспечивает все типы членов и операции, предлагаемые контейнером vector, за исключение индексации, capacity() и reserve().
Слайд 14
Слайд 15
Полезные операции.
Операции с первыми элементами списков созданы в дополнение к операциям, которые имеют дело с последними элементами, обеспечиваемым всем последовательностям.
Первый элемент контейнера называют front.
Оператор fruit.remove(“orange”); улалит все элементы со значением “orange”
"Для устранения дубликатов есть операция..."
Для устранения дубликатов есть операция unique().
"Иногда возникает необходимость посмотреть контейнер..."
Иногда возникает необходимость посмотреть контейнер в обратном порядке. Для списков можно изменить порядок элементов так, чтобы первый стал последним и т. д. Без копирования элементов. Это обеспечивается операцией reverse().
Адаптеры последовательностей.
Контейнеры vector, list, deque не построить один из другого без потери эффективности. С другой стороны, при помощи этих трёх базовых последовательностей можно эффективно реализовать стеки(stack) и очереди(queue). Поэтому классы stack и queue определены не как отдельные контейнеры, а как адаптеры базовых контейнеров.
Стек.
Адаптер stack определён в заголовочном файле < stack>.
По-сути, stack - это тпросто интерфейс для контейнера, тип которого передаётся как аргумент шаблона. Всё что stack делает- это удоляет из интерфейса нестековые операции для своего контейнера и даёт операциям back(), push_ back() и pop_ back() общепринятые имена-соответственно top(), push() и pop().
По умолчанию стек хранит свои элементы в очереди с двумя концами, но можно использовать любую другую последовательность, обеспечивающую операции back(), push_back() и pop_ back() .
Очереди.
Очереди с приоритетом.
Очередь с приоритетом(prioriry_queue)-это такая очередь, где каждому элементу назначен приоритет, определяющий порядок, в котором элементы достигают выхода из очереди(top() ).
Объявление prioriry_queue находится в заголовочном файле <queue>.
Ассоциативные контейнеры.
Ассоциативный массив- это один из самых полезных и универсальных типов, определяемых пользователем.
Ассоциативный массив, часто называемый отображением (map), а иногда словарем(dictionary), содержит пары значений. Зная одно значение, называемое ключом, мы можем получить доступ к другому, называемому отображённым значением.
Ассоциативный массив можно представить как массив, для которого индекс не обязательно должен иметь целочисленный тип.
Ассоциативные контейнеры-это обобщение понятия ассоциативного массива.
Ассоциативный массив map.
map-это последовательность пар(ключ, значение), которая обеспечивает быстрое получение по ключу. Каждому ключу соответствует максимум одно значение, то есть каждый ключ уникален.
Индексация в map.
Типичная операция с ассоциативным массивом-это ассоциативный поиск при помоши оператора индексации([]).
Оператор индексации выполняет поиск по ключу, заданному в качестве индекса, и возврвщает соответствующее значение.
Если ключ не найден, в ассоциативный массив вставляется элемент с этим ключом и значением по умолчанию.
Специализированные операции.
Операция m.find(k) выдаёт итератор, соответствующий элементу с ключом k. Если такого элемента не существует, возвращённым итератором будет m.end().
"Для нахождения сех вхождений используются..."
Для нахождения сех вхождений используются функции m.lower_bound(k) и m.upper_bound(k), которые дают начало и конец подпоследовательности элементов контейнера m, имеющих ключ k. Обычно конец последовательности - это итератор, указывающий на элемент, следующий за последним.
Слайд 28
Операции со списками.
Вводить данные в ассоциативный массив принято простым присваиванием с использованием индексации.
Слайд 30
Слайд 31
multimap.
Контейнер multimap похож на map, если не считать, что он позволяет дублировать ключи.
Множества.
Слайд 34


Заголовок слайда отсутствует

Понятие контейнера.
•Контейнер-это объект, содержащий другие объекты. Примерами контейнеров являются списки и вектора. Как правило, в контейнер можно добавлять объекты и удалять их из него.
В стандартной библиотеке определены два типа контейнеров-последовательности и ассоциативные контейнеры.


Заголовок слайда отсутствует

Ассоциативные контейнеры.
•Ассоциативный массив- это один из самых полезных и универсальных типов, определяемых пользователем. •Ассоциативный массив, часто называемый отображением (map), а иногда словарем(dictionary), содержит пары значений. Зная одно значение, называемое ключом, мы можем получить доступ к другому, называемому отображённым значением. •Ассоциативный массив можно представить как массив, для которого индекс не обязательно должен иметь целочисленный тип. •Ассоциативные контейнеры-это обобщение понятия ассоциативного массива.
Ассоциативный массив.
map-по значению ключа можно однозначно найти значение.
multimap - позволяет дублировать значения для данного ключа.
set и multiset - ключу может не соответствовать никакого значения.


Заголовок слайда отсутствует

Последовательности.
•Стандартная библиотека обеспечивает следующие фундаментальные последовательности:
(вектора(vector)
(списки(list)
(очереди с двумя концами(deque).
•Из них предоставлением соответствующего интерфейса, созданы
(стеки(stack);
(очереди(queue);
(очереди с приоритетами(priority_queue).


Заголовок слайда отсутствует

Вектора.
(Если не утверждается обратное, всё сказанное о векторе относится ко всем стандартным контейнерам.
А
В
С
С
В
A
begin()
end()
rbegin()
rend()
Стандартный вектор-это шаблон, представленный в заголовочном файле <vector>.
При помощи итераторов программист может управлять контейнерами.
Пара итераторов begin()/end() выдаёт элементы контейнера в обычном порядке, т.е. за нулевым следует первый, затем второй и т.д. А пара rbegin()/rend() выдаёт их в обратном порядке, т.е. за n-1 элементом n-2 и т.д.


Заголовок слайда отсутствует

Списки.
•Список-это последовательность, оптимизированная для вставки и удаления элементов. По сравнению с вектором(и deque) обращение к элементу списка по индексу оказалось бы чрезвычайно медленным. Поэтому список обеспечивает двунаправленные итераторы, что приводит к тому, что список, как правило реализуется при помощи некоторой формы двусвязного списка. •Контейнер list обеспечивает все типы членов и операции, предлагаемые контейнером vector, за исключение индексации, capacity() и reserve().
list
Элементы:
rep
rep(representation) - «представление»


Заголовок слайда отсутствует

Оператор fruit.splice(fruit.begin(),citrus); приведёт к удалению всех элементов из citrus и вставку их во fruit.
fruit:
apple pear
citrus:
orange grapfruit lemon
В результате получим fruit:
grapfruit lemon apple orange pear
citrus:
<пусто>
Функция merge() (слияние) объединяет два сортированных списка, удаляя элементы из одного списка и вставляя в другой с сохранением порядка.
f1:
3 4 7 5
f2:
2 1 9
f1.sort();
f2.sort();
f1.merge(f2);
f1:
1 2 3 4 5 7 9
f2:
<пусто>
Если один из объединяемых списков не отсортирован, merge() всё же выдаст список, содержащий объединение элементов этих двух списков. Однако относительно порядка в получившемся списке нет никаких гарантий.


Заголовок слайда отсутствует

Конструкторы.
Размер можно так же указать неявно, задав начальный набор элементов. Это делается путём задания конструктору последовательности значений, из которых конструируется vector.
Например:
vector<int>vi(10);
vector<double>vd(50);
Имя
Так можно создать вектор из 10 целых чисел.
А так из 50 чисел типа double.
vector<int>v1(lst.begin(),lst.end()); //копируем элементы из списка
char p[ ]=“hello”;
vector<char>v2(p,&p[sizeof(p)-1]); //копируем символы из р-строки.
vector<char>v(10,’x’); //v.size==10,каждый элемент имеет значение ‘x’


Заголовок слайда отсутствует

Операции со стеком.
•Функции push_back() и pop_back() обращаются с вектором как со стеком, добавляя элементы в конец и удаляя последние элементы.
S.push_back(‘a’);
Каждый раз при обращении к push_back() вектор s увеличивается на один элемент; этот элемент добавляется в конец.
Функция pop_back() не возвращает значения. Она просто снимает (pop) элемент с вершины стека, и если мы хотим узнать что было на вершине стека перед этим, то должны посмотреть.(bask())


Заголовок слайда отсутствует

Операции, характерные для списков.
•Иногда полезно добавлять элементы в середину вектора или удалять их.
•Рассмотрим эти операции на примере.
Создадим вектор фруктов.
vector<string>fruit;
fruit.push_back(“apple”);
fruit.push_back(“grape”);
fruit.push_back(“kiwifruit”);
fruit.push_back(“peach”);
fruit.push_back(“pear”);
fruit.push_back(“starfruit”);
Операция erase(p1,p2) удаляет элементы с р1-го до р2-го, не включая последний (р1,р2-итераторы).
fruit.erise(fruit.begin()+1,fruit.begin()+4) /* удаляем grape, kiwifruit, peach */
Удаление элементов вектора изменяет его размер, а элементы после удалённого переносятся в освободившиеся позиции, т.е fruit.size() становится равным 3, а pear, бывшая fruit[4], теперь стала fruit[1].
Элемент № 0
Элемент № 5


Заголовок слайда отсутствует

•Можно удалять элементы по одному.
•fruit.erise(find(fruit.begin(),fruit.end(),”pear”));
Так будет найден и удалён элемент “pear”.
fruit.erise(fruit.begin()); А так удалим элемент “apple”.
Элементы можно вставить в вектор.
fruit.insert(fruit.begin()+1,”cherry”);
fruit.insert(fruit.end(),”cranberry”);
Элемент вставляется перед указанной позицией, а элементы до конца сдвигаются, чтобы освободить место.
Полученный вектор
starfruit
cherry
cranberry
F.insert(F.end(),x) эквивалентно F.push_back(x).
Вектор фруктов.
apple
pear
starfruit


Заголовок слайда отсутствует

•Мы можем также вставить целую последовательность.
•
Пусть у нас есть контейнер citrus[]:
lemon
grapefruit
orange
lime
Операция fruit.insert(fruit.begin()+2,citruis.begin(),citruis.end()); даст следующий эффект:
fruit[]
starfruit cherry lemon grapefruit orange lime cranberry
Элементы контейнера citruis копируются в вектор fruit функцией insert(). Значение citruis остаётся неизменным.
Операция clear() удаляет все элементы контейнера.
citruis.clear(); //теперь citruis.size() равно 0


Заголовок слайда отсутствует

Функции-помощники.
•Два вектора можно сравнить при помощи операторов = = и <.
•Два вектора v1 и v2 считаются равными, если v1.size()= =v2.size() и v1[n]= = v2[n] для любого допустимого индекса n.
Аналогичным образом, <-это лексикографическое упорядочивание. Это означает, что x меньше y, если первый x[i], не равный соответствуещему элементу y[i], меньше, чем y[i], или x.size()<y.size() при равенстве всех x[i] соответствующим y[i]. Стандартная библиотека предоставляет операторы !=, <=, > ,>= с определениями, соответствующими = = и <.
Vi1[]
1 23 45 2
Vi2[]
1 23 45 2
Vi3[]
1 23 2
Vi4[]
1 23 45 2
Функция swap(v1,v2) меняет местами два вектора v1 и v2. Используется при сортировке.


Заголовок слайда отсутствует

Полезные операции.
•Операции с первыми элементами списков созданы в дополнение к операциям, которые имеют дело с последними элементами, обеспечиваемым всем последовательностям.
•Первый элемент контейнера называют front.
•Оператор fruit.remove(“orange”); улалит все элементы со значением “orange”
fruit:
apple orange lemon orange pear lime
fruit:
apple lemon pear lime
fruit.remove(“lemon”);
fruit:
apple pear lime


Заголовок слайда отсутствует

•Для устранения дубликатов есть операция unique().
Рассмотрим два случая:
fruit:
apple pear apple apple pear
fruit.unique();
fruit.sort();
fruit.unique();
fruit:
apple pear apple pear
fruit:
apple apple apple pear pear
fruit:
apple pear


Заголовок слайда отсутствует

•Иногда возникает необходимость посмотреть контейнер в обратном порядке. Для списков можно изменить порядок элементов так, чтобы первый стал последним и т. д. Без копирования элементов. Это обеспечивается операцией reverse().
fruit :
banana
cherry
lime
apple
fruit.reverse();
fruit :
apple
lime
cherry
banana


Заголовок слайда отсутствует

Адаптеры последовательностей.
•Контейнеры vector, list, deque не построить один из другого без потери эффективности. С другой стороны, при помощи этих трёх базовых последовательностей можно эффективно реализовать стеки(stack) и очереди(queue). Поэтому классы stack и queue определены не как отдельные контейнеры, а как адаптеры базовых контейнеров.
Адаптер контейнера предоставляет ограниченный интерфейс к контейнеру. В частности, они не предоставляют итераторов; предполагается, что что адаптеры будут использоваться только через их специализированные интерфейсы.


Заголовок слайда отсутствует

Стек.
•Адаптер stack определён в заголовочном файле < stack>.
•По-сути, stack - это тпросто интерфейс для контейнера, тип которого передаётся как аргумент шаблона. Всё что stack делает- это удоляет из интерфейса нестековые операции для своего контейнера и даёт операциям back(), push_ back() и pop_ back() общепринятые имена-соответственно top(), push() и pop(). •По умолчанию стек хранит свои элементы в очереди с двумя концами, но можно использовать любую другую последовательность, обеспечивающую операции back(), push_back() и pop_ back() .
stack<char>s1;
stack<int, vector<int>>s2;
Использует deque<char> для хранения элементов типа char.
Использует vector<int> для хранения элементов типа int.


Заголовок слайда отсутствует

Очереди.
Определённый в заголовочном файле <queue> контейнер queue-это интерфейс для контейнера, позволяющего вставлять элементы в конец(back()) и извлекать элементы из начала(front()). По умолчанию очередь queue для хранения своих элементов использует deque, но для этой цели можетприменяться и любая другая последовательность, предоставляющая операции front(), back(), push_back(), pop_front(). Поскольку вектор не предоставляет операцию pop_front() \, он не может использоваться в качестве базового контейнера для очередей.


Заголовок слайда отсутствует

Очереди с приоритетом.
•Очередь с приоритетом(prioriry_queue)-это такая очередь, где каждому элементу назначен приоритет, определяющий порядок, в котором элементы достигают выхода из очереди(top() ). •Объявление prioriry_queue находится в заголовочном файле <queue>.
По умолчанию priority_value просто сравнивает элементы при помощи оператора <, и top() воэвращает наибольший элемент.


Заголовок слайда отсутствует

В добавление к основным операциям с последовательностями list предоставляет операции, специально приспособленнные для манипулирования списками.
Возьмем два списка
fruit:
apple pear
citrus:
orange grapfruit lemon
Удалить из одного списка и вставить в другой список можно с помощью одной операции удаления-вставки(splice).
list<string>::iterator p=find(fruit.begin(),fruit.end(),”pear”);
fruit.splice(p,citrus.begin()); /*удаляем первый элемент из citruis и вставляем его перед pear в списке fruit*/
В результате получим fruit:
apple orange pear
citrus:
grapfruit lemon


Заголовок слайда отсутствует

Одним из важных аспектов контейнера vector по сравнению с другими контейнерами является лёгкий и эффективный доступ к его отдельным элементам в произвольном порядке.
Доступ к элементам.
Функции-члены front() и back() возвращают ссылки на первый и последний элемент соответственно.
Индексация осуществляется
Функция at() . Осуществляет проверку диапазона и возбуждает исключение out_of_range .
operator[] () . Обеспечивает доступ без проверки.


Заголовок слайда отсутствует

Функции присваивания assign предоставляют альтернативу конструкторам со многими аргументами
Функция присваивания assign.
char s[ ]=“literal”;
vc.assign(s,&s[sizeof(s)-1]); // создали vc и присваиволи vc строку s
Присваивание полностью заменяет элементы вектора:все старые элементы удаляются и вставляются новые. После присваивания размер вектора становится равным числу присвоенных элементов.
v.assign(5,’a’);
Теперь v.size()=5, каждый элемент имеет значение ‘a’


Заголовок слайда отсутствует

Ассоциативный массив map.
•map-это последовательность пар(ключ, значение), которая обеспечивает быстрое получение по ключу. Каждому ключу соответствует максимум одно значение, то есть каждый ключ уникален.
Ассоциативный массив требует, чтобы для типов ключа существовала операция «меньше»; он хранит свои элементы отсортированными(по ключу) так, что перебор происходит по порядку.
Контейнер map обеспечивает обычный набор функций, возвращающих итераторы:
begin()
end()
rbegin()
rend()
К первому элементу любой пары pair обращаемся как к first, а ко второму как к second.


Заголовок слайда отсутствует

Индексация в map.
•Типичная операция с ассоциативным массивом-это ассоциативный поиск при помоши оператора индексации([]). •Оператор индексации выполняет поиск по ключу, заданному в качестве индекса, и возврвщает соответствующее значение. •Если ключ не найден, в ассоциативный массив вставляется элемент с этим ключом и значением по умолчанию.
Пример:
map<string,int> m;
int x = m[“Генри”];
m[“Гарри”] = 7;
int y = m[“Генри”];
m[“Гарри”] = 9;
Тип ключа- string, тип значения-int.
Сначала массив пуст.
Создаём новую запись для “Генри”, инициализируем его нулём и возвращаем 0
Создаём новую запись для “Гарри”, инициализируем его нулём и присваиваем 7
Возвращаем значение из записи “Генри”
Заменяем значение записи “Гарри” на 9


Заголовок слайда отсутствует

Специализированные операции.
•Операция m.find(k) выдаёт итератор, соответствующий элементу с ключом k. Если такого элемента не существует, возвращённым итератором будет m.end().
m.find(k)
Для контейнера с уникальными ключами, такого как map или set, результирующий итератор будет указывать на единственный элемент с ключом k.
Для контейнера с неуникальными ключами, такого как multimap или multiset, результирующий итератор будет указывать на первый элемент с таким ключом.


Заголовок слайда отсутствует

•Для нахождения сех вхождений используются функции m.lower_bound(k) и m.upper_bound(k), которые дают начало и конец подпоследовательности элементов контейнера m, имеющих ключ k. Обычно конец последовательности - это итератор, указывающий на элемент, следующий за последним.
Пример:
void f(multimap<string,int>&m)
{multimap<string,int>::iterator lb = m.lower_bound(“apple”);
multimap<string,int>::iterator ub = m.upper_bound(“apple”);
for (multimap<string,int>::iterator p = lb; p!=ub;++p)
{//…...}
}
Но есть и более эффективный способ
void f(multimap<string,int>&m)
{
typedef multimap<string,int>::iterator MI;
pair<MI,MI> q = m.equal_range(“apple”);
for (MI p = q.first; p!=q.second;++p)
{//…...}
}
Находит верхнюю и нижнюю границы одновременно


Заголовок слайда отсутствует

Если lower_bound(k) не находит k , она возвращает итератор на первый элемент с ключом больше k или end(), если такого элемента не существует. Этот способ сообщения о неудаче также используется функциями upper_bound() и equal_range().


Заголовок слайда отсутствует

Операции со списками.
•Вводить данные в ассоциативный массив принято простым присваиванием с использованием индексации.
Phone_book[“отдел заказов”]=8226339;
Это гарантирует, что об отделе заказов будет занесена соответствующая запись, независимо от того, было ли в телефонной книге что-либо об отделе заказов до того.


Заголовок слайда отсутствует

Также можно вставить элемент функцией insert()
map<key_type,mapped_type> m;
Операция m.insert(val) пытается добавить в m пару val типа (key_type,mapped_type).
Поскольку ассоциативные массивы - это контейнеры с уникальными ключами, вставка производится тольно если в m не существует элемента с таким ключом.
Возвращаемое значение пара pair<iterator,bool>. Переменная типа bool принимает значение true, если val действительно вставилось. Итератор указывает на тот элемент контейнера m, который имеет ключ val.first.


Заголовок слайда отсутствует

Удалять элементы можно с помощью функции erase()
int count = phone_book.erase(“Роман”);
Число удалённых элементов.


Заголовок слайда отсутствует

multimap.
•Контейнер multimap похож на map, если не считать, что он позволяет дублировать ключи.
multimap не может поддерживать индексацию по значению ключа. Операции equal_range(), lower_bound() upper_bound() являются первичными средствами доступа к нескольким значениям с одним и тем же ключом.
В контейнере multimap аргумент операции insert() всегда вставляется , поэтому insert() возрращает итератор, а не пару pair<iterator,bool>.


Заголовок слайда отсутствует

Множества.
Множества(set) можно рассматривать как ассоциативные массивы, в которых значения не играют роли, так что мы отслеживаем только ключи.
Контейнер multiset - это множество, допускающее одинаковые ключи. Операции equal_range(), lower_bound(),upper_bound()- основные средства доступа к нескольким вхождениям ключа.


Заголовок слайда отсутствует

Конец.


Заголовок слайда отсутствует

Лекция № 11
Стандартные контейнеры.
Уральский государственный университет
Кафедра Математической Физики,
Центр технологий дистанционного образования
Курс Лекций
С++ и Объектно-ориентированное
Программирование
Устинов В.А., Кискачи А.В.


Заметки
Структура

Лекция № 2
Инкапсуляция.
Описание класса
Первая программа на С++
Инкапсуляция
Протокол описания Класса
Функции-члены или Методы
Доступ к элементам класса
Спецификаторы доступа
Хороший стиль программирования
Примеры описания Класса
Объявление объектов
(экземпляров класса)
Слайд 11
Конструкторы
Деструкторы
Объекты в качестве членов класса. Списки инициализации
Типичные ошибки программирования
Замечания по технике программирования
Замечание по технике программирования
Класс Банковский Счет
Термины
Задание на практику


No Slide Title

Протокол описания Класса
Ключевые слова:
struct
class
union
Все элемен-ты класса открыты
(public).
Тип доступа не может быть изменен.
Все элемен-ты класса открыты
(public).
Тип доступа может быть изменен.
Все элемен-ты класса скрыты
(private).
Тип доступа может быть изменен.


No Slide Title

Объявление объектов
 (экземпляров класса)
Point A,B,C; // объявление 3 объектов
Bar A(3,6) // объявление с параметрами
Circle D[34] // объявление массива
Bar *D; D=new Bar(6,9); // объявление указателя
A.x = 5; D - > x = 5; // доступ к данным
A.show( ); D -> show( ); // вызов методов
Объявление и определение методов
Существует 2 способа включение метода в класс:
•Определение метода при описании класса
•Объявление метода при описании класса с последующим определением вне описания класса


No Slide Title

Конструкторы
•Методы, выполняющие задачи по инициализации объекта
•Имя метода совпадает с именем класса
•Не возвращают значений (даже типа void)
•Могут либо определяться пользователем либо создаваться по умолчанию
•В конструкторах нередко используются перегрузка и присвоение начальных значений аргументов
•Конструктор не может быть виртуальным
Class Complex {
public:
Complex ( );
Complex (double);
Complex (double, double);
private:
double Re, Im;
};
Complex :: Complex ( )
{Re=0.0; Im=0.0;}
Complex :: Complex (double R)
{Re=R; Im=0.0;}
Complex :: Complex (double R,
double I)
{Re=R; Im=I;}


No Slide Title

Термины
h Данные-члены
h Функции-члены
h Public
h Private
h inline и НЕ inline методы
h Конструктор
h Деструктор
h Начальные значения


No Slide Title

Первая программа на С++
// Вычисление стоимости Пиццы
#include <iostream.h>
void main( ) {
int Number_Dobavok;
int Cost_Dobavok;
int Cost;
// Определение количества добавок
cout << “Сколько добавок вам нужно к пицце? \n”;
cin >> Number_Dobavok;
// Вычисление Стоимости Добавок
Cost_Dobavok=Number_Dobavok * 2;
// Вычисление Полной стоимости пиццы
Cost =10+ Cost_Dobavok;
// Печать результата
cout << “Ваша пицца стоит $” << Cost << “\n”;
}


No Slide Title

Функции-члены или Методы
•Объявление функции включается в объявление структуры (класса) и вызывается с использованием механизма доступа к членам структуры (класса).
•Методы непосредственно могут использовать имена членов-данных.
struct Time
{
int hour;
int minute;
int second;
};
void printTime( );
...
…
main ( )
{ Time k;
k.hour =10;
//можно задать и //неправильное //значение
k.minute =70;
printTime( );
...
}


No Slide Title

Задание на практику
Написать модифицированную программу о
банковском счете
•Учитывать отдельно суммы положенные на счет P и суммы снятые со счета S.
•Учитывать количество обращений к счету n.
•Определить 2 конструктора.
•Определить методы:
•Show показывать S,P,n и разницу P-S, если она меньше нуля
•Compare (private)
•Put, Get
•Часть методов должна быть определена вне класса, а часть - внутри описания класса.


No Slide Title

Класс Банковский Счет
#include <iostream.h>
class Schet { //описание класса
int s,n; //описание данных
public: //модификатор доступа
//объявление и определение методов внутри класса
Schet (int s1=0) {n=0; s=s1;};
void Put (int s1) {s+=s1; n++;};
void Get (int s1); //объявление метода
void Show( ) {cout<<n<<" "<<s<<"\n";};
};
void Schet::Get(int s1) {s-=s1; n++;};
// Определение метода вне класса
void main( ) {
Schet A(5000), B; //описание объектов класса
A.Put(200); A.Show( ); B.Put(50); B.Show( );
A.Get(2000); A.Show( ); B.Put(25); B.Show( );
A.Put(3500); A.Show( ); B.Get(15); B.Show( );
}


No Slide Title

2 способа включения метода в класс:
class A {
int x,y;
int sum( ) //определение
{return (x+y); } // метода
};
Методы, определенные
внутри класса, являются
неявно встроенными.
class Lug {
int x,y;
int sum( ); // объявление
int som( ); // методов
};
// определение вне класса
int Lug::sum( ) {return(x+y);}
inline int Lug::som( )
{return(x-y);}
•Для определения метода вне класса используется оператор разрешения области действия :: .
•Такой метод не обязан быть встроенным


No Slide Title

Примеры описания Класса
class Mina {
int V; //глубина дна
int H; //глубина мины
void Dno( ) {H=V;}
public:
void seth (int h1)
{H=h1; if (H<=0) Dno( );}
int geth( ) {return H};
};
…
Mina A; A.V=15;
A.seth(10);
cout << A.geth( );
A.Dno( );
struct Mina {
int V; //глубина дна
void seth (int h1)
{H=h1; if (H<=0) Dno();}
int geth() {return H;}
private:
int H; //глубина мины
void Dno( ) {H=V;}
};
…
Mina A; A.V=15;
A.seth(10);
cout << A.geth();
A.Dno( );


No Slide Title

Спецификаторы доступа
•Метки public: (открытая) и private: (закрытая) называются спецификаторами доступа к элементам.
•Любые члены-даные и методы, объявленные после спецификатора public: (и до следующего спецификатора доступа к элементам), доступны при любом обращении программы к объекту. Это открытый интерфейс услуг класса, которым будет пользоваться клиентами класса (т.е. частями программы, играющими роль пользователей) для манипуляций с данными этого класса.
•Любые члены-даные и методы, объявленные после спецификатора private: (и до следующего спецификатора доступа к элементам), доступны только методам этого класса.


No Slide Title

Типичные ошибки программирования
hЗабывается точка с запятой в конце определения класса.
hПопытка явно присвоить начальное значение членам-данным в определении класса.
•


No Slide Title

Хороший стиль программирования
h Используйте при определении класса каждый спецификатор доступа к элементам только один раз, что делает программу боле ясной и простой для чтения. Размещайте первыми элементы public, являющиеся общедоступными.


No Slide Title

Замечания по технике программирования
Клиенты имеют доступ к интерфейсу класса, но не имеют доступа к реализации класса.
Объявление методов внутри определения класса и описание методов вне этого определения отделяет интерфейс класса от его реализации. Это способствует высокому качеству разработки программного обеспечения.


No Slide Title

Замечание по технике программирования
Использование принципов ООП часто может упростить вызовы функции за счёт уменьшения числа передаваемых параметров. Это достоинство ООП проистекает из того факта, что инкапсуляция членов-данных и методов внутри объекта даёт методам право прямого доступа к членам-данным.


No Slide Title

class Time
{
public:
void setTime(int,
int, int) ;
void printTime( );
private:
int hour;
int minute;
int second;
};
…
Доступ к элементам класса
…
void Time:: setTime(int h, int m, int s)
{
hour =(h>=0 && h<=24) ? h : 0;
minute =(m>=0 && m<60) ? m : 0;
second =(s>=0 && s<60) ? s : 0;
}
...
main ( )
{ Time k;
k.hour =10;
k.setTime(10, 0, 0);
k.printTime();
}


No Slide Title

Деструкторы
•Методы, выполняющие задачи по уничтожению объекта.
•Имя метода = ~ + имя класса
•Не возвращают значений (даже типа void)
•Могут либо определяться пользователем либо создаваться по умолчанию
•Деструктор всегда один и не имеет аргументов
•Деструктор может быть виртуальным
Class Vector {
public:
Vector ( );
Vector (int N);
~Vector( );
private:
double * X, * Y;
};
Vector :: Vector ( )
{X = new double [2];
Y = new double [2];}
Vector :: Vector (int N)
{X = new double [N];
Y = new double [N];}
Vector :: ~Vector( )
{delete X; delete Y;}


No Slide Title

Объекты в качестве членов класса. Списки инициализации
class Club {
Table members; // список членов
Date founded; // дата основания
….
Club(Date fd) : members( ), founded(fd)
                                                               {
//……..
   }
};
Конструкторы членов вызываются до вызова конструктора класса в порядке объявления в классе. Деструкторы вызываются в порядке обратном порядку вызовов конструкторов.
Если конструктор члена не нуждается в аргументах, член можно не указывать в списке инициализации.
Club(Date fd ) : founded(fd);


No Slide Title

Лекция № 2
Инкапсуляция.
Описание класса
Уральский государственный университет
Кафедра Математической Физики,
Центр технологий дистанционного образования
Курс Лекций
С++ и Объектно-ориентированное
Программирование
Устинов В.А., Кискачи А.В.


Заметки
Структура

Лекция № 3
Наследование.
Производные Классы.
Цели
Определения
Пример наследования
Синтаксис
Базовые и производные классы
Базовые и производные классы более подробно
Атрибуты доступа
Наследование и доступ к элементам класса-наследника
Конструктор класса-потомка
Слайд 11
Слайд 12
Слайд 13
Слайд 14
Слайд 15
Слайд 16
Особенности Наследования
Совет по повышению эффективности
Пример. файл «point.h»
Пример. файл «point.cpp»
Пример. «main»
Термины
Задание на практику


С++ и Объектно-ориентированное Программирование

Синтаксис
class <имя класса> : [ public | protected | private ]
<имя базового класса>
{
//объявления членов
};
class Point {
protected:
int X, Y, Color;
public:
Point ( ) ;
Point(int x1, int y1, int c1);
void Show( );
int Getc ( ) {return Color;};
};
class Circle : public Point {
protected:
int R;
public:
Circle (int x1, int y1,
int c1, int r1);
void Show( );
};


С++ и Объектно-ориентированное Программирование

Особенности Наследования
•Для вызова методов предка из методов наследника следует использовать оператор ::
Circle::Show( ) {… Point::Show( ); … }
•Указатель на базовый класс может быть использован для указания на производный класс.
Point *A; Point *X; Circle *B;
A =new Point(10, 20, 12); B =new Circle (5, 7, 12, 4);
A - >Show( ); B - >Show( );
…
if (i>0)
 X=new Point(10,10,1); else X=new
 Circle(10,10,1,2);
X - > Show( );
Какой метод Show будет вызван ?


С++ и Объектно-ориентированное Программирование

Термины
h Базовый класс
h Производный класс
h Перегрузка и
h перекрытие методов
h Модификатор доступа
h Protected


С++ и Объектно-ориентированное Программирование

Задание на практику
•Написать модифицированную программу о реализации классов Location, Point, Circle, Bar, используя механизм наследования.
•Реализовать методы :
¨Show - показывать
¨Hide - скрывать
¨MoveTo - переместить
•Продемонстрировать создание объектов Point, Circle, Bar и их движение.
•Исследовать возможность работы с производными классами указателей на базовый класс.
•


С++ и Объектно-ориентированное Программирование

Пример. файл «point.h»
class Location {
protected:
int X, Y;
public:
Location (int x0=10, int y0=20) {X=x0; Y=y0;};
};
class Point : public Location {
protected:
int Color;
public:
Point (int x0, int y0, int c0);
void Show (void);
void Hide (void);
void MoveTo (int nx, int ny, int nc);
};


С++ и Объектно-ориентированное Программирование

Цели
•Научиться создавать новые классы путём наследования существующих классов.
•Понять, как наследование способствует повторному использованию программного обеспечения.
•Понять формы записи базовых классов и производных классов.


С++ и Объектно-ориентированное Программирование

Пример. файл «point.cpp»
#include <graphics.h>
#include <conio.h>
#include "point.h"
Point :: Point (int x0, int y0, int c0) :
Location (x0, y0) {Color =c0; }
void Point :: Show (void) { putpixel(X, Y, Color) ; }
void Point :: Hide (void) { putpixel(X, Y, getbkcolor( )); }
void Point :: MoveTo (int nx, int ny, int nc)
{Hide( );
X=nx; Y=ny; Color=nc;
Show( );
}


С++ и Объектно-ориентированное Программирование

Пример. «main»
void main( ) {
int gd =VGA, gmode;
initgraph(&gd, &gmode, "c:\\compil\\bc2\\bgi " );
Point A(10,20,13), B(100,50,7);
A.Show( ); getch( );
B.Show( ); getch( );
A.MoveTo(100, 10,12); getch( );
B.Hide( ); getch( );
B.Show ( ); getch( );
B.MoveTo(110, 60,14); getch( );
closegraph( );
}


С++ и Объектно-ориентированное Программирование

Определения
Наследование - это
•механизм, позволяющий объектам класса наследовать характеристики (данные и методы) более простых и общих типов (классов);
•средство получения новых классов из существующих (базовых классов).
Point
Rectangle
Circle


С++ и Объектно-ориентированное Программирование

Типичные ошибки программирования
hРассмотрение объектов базового класса как объектов производного класса может вызвать ошибки .
hПрисваивание объекта производного класса объекту соответствующего базового класса и затем попытка сослаться в этом новом объекте базового класса на элементы, имеющиеся только в объектах производного класса.
•


С++ и Объектно-ориентированное Программирование

Типичная ошибка программирования
•При переопределении в производном классе метода базового класса принято вызывать версию базового класса и после этого выполнять некоторые дополнительные операции. При этом ссылка на метод базового класса без использования операции разрешения области действия может вызвать бесконечную рекурсию, потому что метод производного класса будет в действительности вызывать сам себя.
•


С++ и Объектно-ориентированное Программирование

Замечания по технике программирования
При переопределении в производном классе функции-члена базового класса нет необходимости получать такую же сигнатуру, как у функции-члена базового класса.
Создание производного класса не влияет на исходный или объектный код базового класса; сохранность базового класса оберегается наследованием.


С++ и Объектно-ориентированное Программирование

Совет по повышению эффективности
•Если классы, полученные путем наследования, более широкие, чем необходимо, это ведет к потере ресурсов памяти и производительности. Наследуйте из класса только самое необходимое.
•


С++ и Объектно-ориентированное Программирование

Замечания по технике программирования
Изменения в базовом классе не требуют изменений в производных классах до тех пор, пока открытый интерфейс базового класса остается неизменным. Однако, производные классы могут нуждаться при этом в перекомпиляции.
Производный класс не имеет прямого доступа к закрытым элементам своего базового класса.


С++ и Объектно-ориентированное Программирование

Базовые и производные классы
• При создании нового класса вместо написания полностью новых членов-данных и методов программист может указать, что новый класс является наследником членов-данных и методов ранее определенного базового класса. Этот новый класс называется производным классом.
• Производный класс обычно добавляет свои собственные члены-данные и методы, так что производный класс в общем случае больше своего базового класса.
• Каждый объект открыто порожденного класса является также объектом соответствующего базового класса. Однако, объект базового класса не является автоматически объектом производного класса!


С++ и Объектно-ориентированное Программирование

Пример наследования
черепаха
Черепаха морская
Черепаха степная
Базовый класс описывает общие черты объектов. Все классы, порожденные базовым классом, наследуют возможности этого базового класса. В процессе объектно-ориентированного проектирования разработчик отыскивает общие черты объектов и выражает их в форме базового класса.


С++ и Объектно-ориентированное Программирование

Конструктор класса-потомка
•При вызове конструктора производного класса вызывается конструктор базового класса.
Point::Point (int x1, int y1, int c1) {X=x1; Y=y1; Color=c1;}
Circle::Circle (int x1, int y1, int c1, int r1) :
Point (x1,y1,c1) { R=r1;}
или
Circle::Circle (int x1, int y1, int c1, int r1) : Point( ) {R=r1;}
•При создании производного класса вызывается конструктор базового класса, а за ним конструктор производного класса.
•При разрушении производного класса вначале вызывается его деструктор, а затем деструктор базового класса.


С++ и Объектно-ориентированное Программирование

Атрибуты доступа
Public
Открытые элементы базового класса доступны
а) внутри класса;
б) вне класса;
в) в производном классе.
Private (действует по умолчанию)
Закрытые элементы базового класса доступны
внутри базового класса и недоступны
а) вне класса;
б) в производном классе.
Protected
Защищенные элементы базового класса доступны
а) внутри класса;
б) в производном классе
и недоступны вне класса.


С++ и Объектно-ориентированное Программирование

Наследование и доступ к элементам класса-наследника
Модификатор Доступ в базовом Доступ в производном
доступа классе классе
______________________________________________________
public public public
protected protected
private недоступен
______________________________________________________protected public protected
protected protected
private недоступен
_____________________________________________________
private public private
protected private
private недоступен


С++ и Объектно-ориентированное Программирование

Point *pointPtr, P(3, 5,10);
Circle *circlePtr, C(4, 6,12, 9);
//присваивание адреса окружности указателю pointPtr
pointPtr=&C;
pointPtr- >Show();
//что будет изображено?
// приведение типов (базового к производному)
circlePtr=(Circle*) pointPtr;
circlePtr- >Show();
// а на этот раз?
// опасность рассмотрения Point как Сircle…
pointPtr=&P;
circlePtr=(Circle*) pointPtr;
circlePtr- >Show();
// что теперь видим на экране?


С++ и Объектно-ориентированное Программирование

Типичная ошибка программирования
•Приведение типа указателя базового класса к типу указателя производного класса может вызвать ошибки, если этот указатель используется затем для ссылки на объект базового класса, который не имеет требуемых элементов производного.


С++ и Объектно-ориентированное Программирование

class Base {
public:
int X, Y;
…
};
class Heir : public Base{
public:
int Z;
...
};
void main( )
{Base A;
Heir B;
A.X=A.Y=10;
A.Z=5;
B.Z=5;
B.X=B.Y=15;
}
Не объект Heir.
Не может обращаться к
его элементам
Объект Base. Может обращаться к
его элементам
Базовые и производные классы более подробно


С++ и Объектно-ориентированное Программирование

Лекция № 3
 Наследование.
 Производные Классы.
Уральский государственный университет
Кафедра Математической Физики,
Центр технологий дистанционного образования
Курс Лекций
С++ и Объектно-ориентированное
Программирование
Устинов В.А., Кискачи А.В.


Заметки
Структура

Лекция № 4
Множественное
Наследование
Цели
Научиться использовать множественное наследование для порождения классов из основных классов.
Понять, как разрешается конфликт имен.
Научиться предотвращать появление двух или более копий в любом следующем производном классе, который косвенно наследует базовый класс.
Определения
Синтаксис
Особенности
Множественного Наследования
Пример. класс MyCircle
Пример. файл «message.h»
Методы класса Message
Методы класса MyCircle
Многократное наследование
Виртуальные базовые классы
Слайд 12
Типичная ошибка программирования
Слайд 14
Термины
Задание на практику


No Slide Title

Виртуальные базовые классы
•Многократного наследования класса можно избежать, используя виртуальные базовые классы.
class A { … } ;
class B : virtual public A { … } ;
class C : virtual public A, public B { … } ;
Объект класса С
члены-данные С
Подобъект класса A
члены-данные A
Подобъект класса B
члены-данные B
Подобъект класса A
члены-данные A
Объект класса С
члены-данные С
Указатель на
Подобъект класса A
Подобъект класса B
члены-данные B
Указатель на
Подобъект класса A
Подобъект класса A
члены-данные A


No Slide Title

Термины
h Множественное
наследование
h Конфликт имен
h Многократное
наследование
h Виртуальный
h Базовый класс


No Slide Title

Задание на практику
•Написать программу о реализации классов MyCircle.
•Продемонстрировать создание объектов MyCircle и их движение.
•Переделать программу с использованием виртуальных базовых классов. Вычислить размер объекта MyCircle для двух вариантов программы.
•Построить пример, иллюстирующий возмож-ность возникновения конфликта имен при множественном наследовании и способ его разрешения.
•


No Slide Title

Методы класса Message
Message::Message(int x0,int y0,int f1,int size1,char *msg1)
: Location(x0,y0) { font =f1; size =size1; msg =msg1;}
void Message::Show(void) {
int s=size/ (8*strlen(msg)); setcolor(10);
settextjustify(CENTER_TEXT,CENTER_TEXT);
settextstyle(font,0,s); outtextxy(X,Y,msg); }
void Message::Hide(void) {
int s=size/(8*strlen(msg)); setcolor(getbkcolor());
settextjustify(CENTER_TEXT,CENTER_TEXT);
settextstyle(font,0,s); outtextxy(X,Y,msg); }
void Message::MoveTo(int x1,int y1) {
Message::Hide( );
X=x1; Y=y1;
Message::Show( ); }


No Slide Title

Многократное наследование
•Запрещено непосредственно наследовать базовый класс в производный более одного раза, т.е. Имя класса в списке базовых не может повторяться.
class A { … };
class B : A, A { … }; // Ошибка
•Однако базовый класс может быть косвенно унас-ледован производным классом более одного раза.
class A {
public:
int X;
… };
class B : public A {… };
class C : public A ,
public B {…};
C object;
object.X = 1;
// Какой X использовать ?
// Унаследованный непосред-// твенно или через класс B?
A::X или B::X;


No Slide Title

Пример. файл «message.h»
class Message :
public Location
{
protected:
char *msg; // сообщение
int font; // шрифт
int size; // размер
public:
Message (int x0, int y0, int f1,
int size1,char *msg1);
void Show(void);
void Hide(void);
void MoveTo(int x1, int y1);
};
class MyCircle: public Circle,
public Message
{
public:
MyCircle(int x0, int y0, int c0,
int r0, int f1, int size1,
char *msg1);
void Show(void);
void Hide(void);
void MoveTo(int x1,int y1,
int c1);
};


No Slide Title

Методы класса MyCircle
MyCircle::MyCircle(int x0, int y0, int c0, int r0, int f1,
int size1, char *msg1):
Circle(x0, y0, c0, r0), Message(x0, y0, f1,size1, msg1)
{... };
void MyCircle::Show( )
{
Circle::Show( ); Message::Show( );
}
void MyCircle::Hide( )
{
Message::Hide( ); Circle::Hide( );
}
void MyCircle::MoveTo(int x1, int y1)
{
Circle::MoveTo(x1, y1); Message::MoveTo(x1, y1);
}


No Slide Title

Синтаксис
class <имя класса> :
[ public | protected | private ] <имя базового класса1>,
[ public | protected | private ] <имя базового класса2>, ...
{
//объявления членов
};
class Base1 {
protected:
int X;
… };
class Base2 {
protected:
int X;
… };
class Prod : public Base1,
public Base2 {
public:
//void Print ( ) {cout<<X;}; ?
void Print ( ) {
cout <<Base1::X;
cout <<Base2::X;}
… };


No Slide Title

Определения
Множественное наследование позволяет порожден-ному классу наследовать более, чем от одного базово-го класса. (Класс может иметь более одного родителя)
черепаха
Черепаха морская
Черепаха степная
Линейное наследование
лошадь
птица
пегас
Множественное
наследование


No Slide Title

Особенности
Множественного Наследования
•Если один или более элементов двух базовых классов имеют одинаковые имена, то в порожденном классе может возникнуть конфликт имен, который разрешается при помощи оператора разрешения контекста ::. (Base1::X)
•Базовые классы инициализируются в порядке объявления.
Base1::Base1(int w) {x =w;}
Base2::Base2(int h) {x =h;}
Prod::Prod (int w, int h): Base1(w), Base2(h) { …}
•Конструкторы, не указанные явно в инициализирующем списке вызываются после явно указанных конструкторов в порядке объявления


No Slide Title

Пример. класс MyCircle
Для примера рассмотрим класс, созданный из окружности ( класс Circle) и строки текста ( класс Message), который должен представлять собой текст в окружности.
Location
Point
Rectangle
Circle
Location
Point
Message
Circle
MyCircle


No Slide Title

Хороший стиль программирования
h Множественное наследование - мощное свойство, но оно может внести в систему сложности. Требуется большая осторожность при проектировании систем для извлечения выгоды из использования множественного наследования; использовать его не следует, если можно ограничиться простым наследованием.


No Slide Title

Цели
•Научиться использовать множественное наследование для порождения классов из основных классов.
•Понять, как разрешается конфликт имен.
•Научиться предотвращать появление двух или более копий в любом следующем производном классе, который косвенно наследует базовый класс.


No Slide Title

Виртуальные базовые классы
•Если базовый класс объявлен как виртуальный, то при определении конструктора класса-наследника нужно явно активизировать конструктор виртуального базового класса. Если этого не сделать, то конструктор виртуального базового класса активизируется неявно со значениями по умолчанию. Если виртуальный базовый класс не снабжен конструктором с умолчанием, компилятор генерирует сообщение об ошибке.


No Slide Title

Типичная ошибка программирования
hЗабывают при определении конструктора класса-наследника явно активизировать конструктор виртуального базового класса.


No Slide Title

Лекция № 4
 Множественное
Наследование
Уральский государственный университет
Кафедра Математической Физики,
Центр технологий дистанционного образования
Курс Лекций
С++ и Объектно-ориентированное
Программирование
Устинов В.А., Кискачи А.В.


‹верхний колонтитул›
‹дата/время›
Click to edit Master text styles
Second level
Third level
Fourth level
Fifth level
‹нижний колонтитул›
‹#›
•Для вызова нужной виртуальной функции используется таблица виртуальных методов (массив, содержащий указатели на функции). У каждого класса, содержащего виртуальные функции, имеется таблица виртуальных методов. Для каждой виртуальной функции в классе таблица имеет элемент, содержащий указатель на вариант виртуальной функции, используемый в объектах данного класса.


Заметки
Структура

Лекция № 5
Полиморфизм.
Виртуальные функции
Цели
Понять способы записи полиморфизма.
Понять как объявлять и использовать виртуальные функции для полиморфизма.
Понять различие между абстрактными и конкретными классами.
Научиться объявлять чистые виртуальные функции для создания абстрактных классов.
Понять, почему полиморфизм делает системы легко расширяемыми.
Определения
Свойства :
Перегрузка функций
Перекрытие методов
Виртуальные функции и позднее связывание
Перегрузка операций
Семейства и шаблоны классов
Выбор методов с одинаковым именем на этапе компиляции
Два способа связывания
Недостатки раннего связывания
Виртуальные Функции
Слайд 9
Виртуальные Функции более подробно.
Замечания по технике программирования
Хороший стиль программирования
Замечание по технике программирования
Слайд 14
Слайд 15
Хороший стиль программирования
Слайд 17
Работа Виртуальных Функций
Пример использования Виртуальных функций
Абстрактные функции и классы
Слайд 21
Замечание по технике программирования
Слайд 23
Достоинства и недостатки Виртуальных Функций
Термины
Задание на практику


No Slide Title

Термины
• Раннее и Позднее связывание
• Виртуальные функции • Абстрактные классы


No Slide Title

Задание на практику
•Дополнить классы Point, Circle, Rectangle, и др. виртуальным методом Area( ), вычисляющим площадь данной фигуры.
•Модернизировать виртуальные методы Show и Hide для вывода значений площади одновременно с рисованием фигуры.
•Отладить тестовый графический пример, включающий вывод любых трех различных геометрических фигур и их движение.
•Привести пример действующей программы, которую невозможно реализовать без виртуальных функций.
•


No Slide Title

Недостатки раннего связывания
Какой метод Show
использовать определяется
на этапе компиляции
•Для добавления нового класса необходима перекомпиляция программы
•При использовании указате-лей возможна ситуация когда компилятор не в состоянии определить какой метод вызвать.
Point * x[3]; x[0] = new Point;
x[1] =new Circle; x[2] =new Bar;
for (int k = 0; k < 3; i++)
x[k] -> Show( );
//Для всех объектов будет вызван
// Point::Show( ) !!!


No Slide Title

Выбор методов с одинаковым именем на этапе компиляции
1. Имеются отличия в типах данных или кол- личестве аргументов в объявлении функции
Show(int, char); Show(double, char); Show(int);
2. Явно указана область действия метода
Point::Show( );Circle::Show( );Rectangle::Show( );
3. Объект класса идентифицирует метод
… Point A; Circle B,C; Rectangle D;
… A.Show( );
 B.Show( );
 C.Show( );
 D.Show( );


No Slide Title

Два способа связывания
Раннее или статическое связывание
- выбор метода на этапе компиляции
Позднее или динамическое связывание
- выбор метода на этапе исполнения программы
Реализуется при помощи
Виртуальных Функций


No Slide Title

Работа Виртуальных Функций
void Point::MoveTo(int x1, int y1)
{ Hide( );
X=x1; Y=y1;
Show( ); }
void Circle::MoveTo(int x1, int y1)
{ Hide( );
X=x1; Y=y1;
Show( ); }
… Point A(10,20,12); Circle B(20,40,10,13); A.Show( ); B.Show( );
A.MoveTo(100,20); B.MoveTo(100,40);


No Slide Title

Объект класса Point
Таблицы виртуальных функций:
Код функций:
protected:
double X, Y;
public:…
virtual void Show();
virtual void Hide();
. . .
Point::Show()
Point::Hide()
Объект класса Circle
private:
double R;
public:…
void Show();
void Hide();
. . .
Circle::Show()
Circle ::Hide()
•Для вызова нужной виртуальной функции используется таблица виртуальных методов (массив, содержащий указатели на функции). У каждого класса, содержащего виртуальные функции, имеется таблица виртуальных методов. Для каждой виртуальной функции в классе таблица имеет элемент, содержащий указатель на вариант виртуальной функции, используемый в объектах данного класса.


No Slide Title

Пример использования Виртуальных функций
class Point {
protected:
double X, Y;
public:
…
virtual double Area( )
{ return(0);}
};
class Bar: public Point {
private:
double H, W;
public:
…
double Area( ) { return(H*W);}
};
class Circle: public Point {
private:
double R;
public:
…
double Area()
{ return(M_PI*R*R);}
};
Point *A, *B, *C;
A =new Point;
B =new Circle;
C =new Bar;
…
S =A->Area()+B->Area()+
C->Area();


No Slide Title

Достоинства и недостатки Виртуальных Функций
+Гибкость при написании программы.
+Облегчают использование и модификацию кода.
+Изменение свойств объекта без перекомпиляции всей программы.
+Исключение повторяющегося кода.
+Возможность разработки многократно используемых классов и методов
+Увеличение надежности программ; Разработка закрытых библиотек.
-Уменьшение производительности.
-Не производится проверка соответствия типов аргументов.


No Slide Title

Полиморфизм - в С++ механизм, позволяющий использовать одинаковые Имена, для сходных по смыслу действий и методов, относящихся к различным объектам (типам и классам) и разных технически.
Определения
Show()
Circle
Point


No Slide Title

Абстрактные функции и классы
•Если при определении базового класса объявлена некоторая виртуальная функция, то она должна быть определена подобно обычным функциям-членам, либо объявлена как абстрактная или «чистая» виртуальная функция при помощи спецификатора = 0.
class Base {
virtual void fun(int) = 0;
… };
•Класс, содержащий хотя бы одну абстрактную функцию, называется абстрактным классом.
•Абстрактный класс можно использовать только в качестве базового для других классов.
•Объекты абстрактного базового класса не могут быть реализованы, хотя можно объявить указатель.


No Slide Title

Виртуальные деструкторы
• При использовании полиморфизма для обработки динамически размещенных объектов иерархии классов может появиться одна проблема. Если объект уничтожается явным использованием операции delete над указателем базового класса на объект, то вызывается деструктор базового класса данного объекта. Это происходит вне зависимости от типа объекта, на который указывает указатель базового класса и вне зависимости от того факта, что деструкторы каждого класса имеют разные имена.
•Решение: объявление деструктора базового класса виртуальным. Это автоматически приведет к тому, что все деструкторы производных классов станут виртуальными.


No Slide Title

• Каждый объект класса, содержащего виртуальные функции, имеет указатель на таблицу виртуальных методов этого класса, недоступный для программиста. Во время выполнения программы вызовы виртуальных функций осуществляются через разыменование соответствующего указателя в таблице виртуальных методов. Просмотр таблицы виртуальных методов и операция разыменования указателя требуют небольших затрат времени выполнения.
Виртуальные Функции более подробно.


No Slide Title

Цели
•Понять способы записи полиморфизма.
•Понять как объявлять и использовать виртуальные функции для полиморфизма. •Понять различие между абстрактными и конкретными классами. •Научиться объявлять чистые виртуальные функции для создания абстрактных классов. •Понять, почему полиморфизм делает системы легко расширяемыми.


No Slide Title

Замечания по технике программирования
Если функция однажды объявлена виртуальной, то она остается виртуальной на любом более низком уровне иерархической структуры.
Если в производном классе решено не описывать виртуальную функцию, то производный класс непосредственно наследует описание виртуальной функции из базового класса.


No Slide Title

Замечание по технике программирования
Переопределенная виртуальная функция должна иметь тот же самый тип возвращаемого значения и ту же самую сигнатуру, что и виртуальная функция базового класса.


No Slide Title

Типичная ошибка программирования
hЕсли в производном классе переопределяется виртуальная функция базового класса и данная функция не имеет тот же тип возвращаемого значения и ту же сигнатуру, что и соответствующая функция базового класса, то возникает синтаксическая ошибка.
•


No Slide Title

Хороший стиль программирования
h Несмотря на то, что некоторые функции могут быть неявно виртуальными, поскольку они объявлены такими на более высоком уровне иерархии, некоторые программисты предпочитают явно объявлять функции виртуальными на каждом уровне иерархии, чтобы обеспечить ясность программы. (Но некоторые компиляторы С++ не допускают повторное использование модификатора virtual).
h


No Slide Title

Типичная ошибка программирования
hПопытка создать объект абстрактного класса (т.е. Класса, который содержит хотя бы одну чистую виртуальную функцию) является синтаксической ошибкой.
•


No Slide Title

Абстрактные функции и классы
•Единственным назначением абстрактного класса является создание соответствующего базового класса, от которого другие классы могут унаследовать интерфейс и реализацию.
•Классы, объекты которых могут быть реализованы, называются конкретными классами.
•В производных от абстрактного классах абстрактные функции должны быть либо определены, либо вновь объявлены как абстрактные.


No Slide Title

Замечание по технике программирования
Если класс является производным от класса с чистой виртуальной функцией и если эта чистая виртуальная не описана в производном классе, тогда функция остается чистой виртуальной и в производном классе. Следовательно, такой производный класс также считается абстрактным классом.


No Slide Title

Свойства :
•Перегрузка функций
•Перекрытие методов
•Виртуальные функции и позднее связывание
•Перегрузка операций
•Семейства и шаблоны классов


No Slide Title

Хороший стиль программирования
h Если у класса имеются виртуальные функции, предусматривайте создание виртуального деструктора, даже если он не требуется этому классу. Классы, производные от данного класса могут содержать деструкторы, которые должны вызываться соответствующим образом.


No Slide Title

Типичная ошибка программирования
hОбъявление конструктора виртуальной функцией. Конструкторы не могут быть виртуальными.
•


No Slide Title

virtual void Show( ); virtual void Hide( );
Виртуальные Функции
• Функция объявляется виртуальной с помощью ключевого слова virtual, предшествующего прототипу функции в базовом классе. Например:
Позднее виртуальная функция может быть замещена в производном классе.
virtual


No Slide Title

Лекция № 5
 Полиморфизм.
Виртуальные функции
Уральский государственный университет
Кафедра Математической Физики,
Центр технологий дистанционного образования
Курс Лекций
С++ и Объектно-ориентированное
Программирование
Устинов В.А., Кискачи А.В.


‹верхний колонтитул›
‹дата/время›
Click to edit Master text styles
Second level
Third level
Fourth level
Fifth level
‹нижний колонтитул›
‹#›
Имена параметров могут быть включены в прототип функции с целью документирования. Компилятор эти имена игнорирует. Прототип функции, размещенный вне описания какой-то другой функции, относится ко всем вызовам данной функции, появляющимся после этого прототипа в данном файле. Прототип функции, размещенный внутри описания некоторой другой функции, относится только к вызовам внутри этой функции.


Заметки
Структура

Лекция № 6
Не ОО особенности С++,
Потоки ввода-вывода
Комментарии
Прототипы функции
Слайд 4
Типичные ошибки программирования
Классы памяти.
Слайд 7
Потоковый Ввод-Вывод
Элементы иерархии классов iostream
Типичные ошибки программирования
Типичная ошибка программирования
Совет по повышению эффективности
Чтение и запись в файл
Полезные советы для чтения и записи файлов
Пример чтения-записи
Совет по повышению эффективности
Форматный Вывод
Примеры форматирования
Манипуляторы
Некоторые манипуляторы
Слайд 21
Термины
Задание на практику
Хороший стиль программирования
Замечание по технике программирования


No Slide Title

Термины
•Прототип функции
•Поток ввода-вывода
•Чтение-запись в файл
•Флаги форматирования
•Манипуляторы


No Slide Title

Задание на практику
•Продемонстрировать возможности ввода и вывода в файл чисел, слов, строк.
•Продемонстрировать возможности форматированного вывода.
•Вывести в файл и на экран таблицу значений y=x2 на промежутке [0,10] с шагом 1.
•Считать эти значения из файла и вывести эту таблицу в восьмеричной системе.
•Считать из файла текст, определить и вывести на экран количество слов.
•


No Slide Title

Полезные советы для чтения и записи файлов
•Следует избегать записи и чтения одного и того же файла одновременно.
•При записи в файл чисел с помощью << необходимо иметь в виду, что числа хранятся в символьном представлении (как текст) но без пробелов между символами.
•В конец файла помещается специальный символ eof. Существует функция-метод eof( ), которую можно использовать для определения конца файла.
•Строки читаются до обнаружения разделителя (eof или \n).
•При чтении чисел в потоке за последним числом в файле символ конца файла отсутствует.


No Slide Title

Потоковый Ввод-Вывод
•Поток - это абстрактный тип данных, представляю-щий некую последовательность элементов данных, направленных от источника к потребителю.
•В С++ потоковый ввод-вывод реализован в библиотеке классов iostream.
•Каждый поток имеет один из трех режимов доступа:
•только для чтения
•только для записи
•для чтения и записи
•C++ поддерживает 4 предопределенных потоковых объекта
•cin - стандартный ввод
•cout - стандартный вывод
•cerr - стандартный вывод ошибок
•clog - буферизированная версия cerr
*


No Slide Title

Пример чтения-записи
#include <fstream.h>
#include <iostream.h>
void main ( ) {
ofstream of1(“fi1.txt”); // открыть файл для записи
of1 << “You must press any key”; // вывод в файл
of1.close( ); of1.open(“fi2.txt”); //закрыть и открыть
of1 << 15 << “ “<< 42 << “ “ << 1; of1.close( );
ifstream inf1(“fi1.txt”); // открыть файл на чтение
char p[50]; inf1 >> p; cout << p << “\n”;
inf1 >> p; cout << p << “\n”; inf1.close( );
int n; inf1.open(“fi2.txt”); // чтение чисел
while ( !inf1.eof() )
{ inf1 >> n;
if ( !inf1.eof() ) cout << n << “\n”; }
inf1.close( );
}


No Slide Title

Возможно использование дух видов комментариев
/* обычные, продолжающиеся
на другой строке */ и
// однострочные, продолжающиеся до конца строки
Описание переменных внутри блока
Комментарии
Возможно описывать переменные в любой точке блока перед их использованием.
for ( int I=0; I<10; I++) { …. }
{
float x=5;
}


No Slide Title

• Способы описания прототипов. void func1 (int, double );
void func2 (int N, double X);
/* Имена параметров могут быть включены в прототипы */
double Show ( ); /* означает */ double Show(void);
void func3( int, …) ;
/* первый аргумент типа int , тип и количество остальных могут варьироваться */
•В прототипах функций можно задавать значения аргументов по умолчанию.
void funс4(int x=10, int y=4); void funс4(int=10, int=4);
•При задании начальных значений начинать надо справа!
void Draw( int x, int y=200, int r); // ошибка
*
Имена параметров могут быть включены в прототип функции с целью документирования. Компилятор эти имена игнорирует.
Прототип функции, размещенный вне описания какой-то другой функции, относится ко всем вызовам данной функции, появляющимся после этого прототипа в данном файле. Прототип функции, размещенный внутри описания некоторой другой функции, относится только к вызовам внутри этой функции.


No Slide Title

Элементы иерархии классов iostream
Обеспечивают буферизацию потокового ввода-вывода и обращение к физическим устройствам ввода-вывода
Реализация форматированного и неформатированного ввода-вывода на высоком уровне, контроль и изменение состояния потока.


No Slide Title

Чтение и запись в файл
#include <fstream.h>
/* необходимо включить в программу для использования операций потокового ввода-вывода в файл */
// открытие файла для записи:
ofstream out1(“file1.txt”);
out1 << “У Попа была собака”; // запись в файл
// открытие файла для чтения
ifstream in1( “file2.txt”);
char bufer[100];
int n;
in1 >> bufer; // чтение текста из файла
in1 >> n; // чтение числа из файла
in1.close( ); // закрыть файл
in1.open(“file3.txt” ); // открыть новый файл


No Slide Title

Форматный Вывод
•Форматирование определяется набором 15 флагов форматирования для объектов ios.
•Флаги форматирования указывают для каждого активного потока
•базу преобразования (10,8,16)
•символы-заполнители
•формат чисел с плавающей запятой
•надо ли игнорировать пробелы при вводе
•ширину поля
•правое или левое выравнивание и др.
•Флаги форматирования можно проверять, устанавливать и очищать посредством различных способов
•вызов функций-методов для объектов потока
•применение манипуляторов


No Slide Title

Примеры форматирования
int pw, I=87;
pw=cout.width(7); // устанавливает Шаблон вывода=7
// и сохраняет предыдущий
cout.fill(‘*’); //устанавливает * в качестве заполнителя
cout << I <<“\n”; // отображает ***** 87 и
//сбрасывает ширину шаблона в 0
cout.width(pw); // восстановление старой ширины
cout << setw (4) << I << setw (6) << j; // тоже что и
cout.width(4);
cout << I;
cout.width(6);
cout << j;


No Slide Title

Манипуляторы
•Манипуляторы - это специальные механизмы или функции, которые могут объединяться в цепочку вставок или извлечений из потока для того, чтобы изменить состояние (флаги) потока.
•Для использования манипуляторов программа должна включать файл <iomanip.h>
•Пользователи могут создавать собственные манипуляторы потоков. Например
ostream & tab (ostream & output)
{ return output << ‘ \t’; }


No Slide Title

Прототип функции указывает компилятору тип данных, возвращаемых функцией, количество и тип параметров, которое ожидает функция, и ожидаемый порядок их следования.
Компилятор использует прототип для проверки правильности вызова функций.
Прототипы функции
Подключение стандартных библиотечных функций осуществляется директивой #include.
<assert.h> <ctype.h> <errno.h> <float.h> <limits.h>
<locale.h> <math.h> <setjmp.h> <signal.h>
<stdarg.h> <stddef.h> <stdio.h> <stdlib.h>
<string.h> <time.h>


No Slide Title

Класс памяти идентификатора определяет период, в течении которого этот идентификатор существует в памяти. В С++ имеется четыре спецификации класса памяти: auto, register, extern, static.
register int counter=1
Классы памяти.
Нельзя использовать несколько спецификаций для одного идентификатора. Современные компиляторы способны распознавать часто используемые переменные и самостоятельно размещать в регистры.


No Slide Title

Типичная ошибка программирования
hЕсли не обеспечивается достаточно широкое поле для обработки выходных данных, то каждое выходное значение печатается с такой шириной поля, которая необходима для его полного представления; это может вызвать сложности при чтении выходных данных.
•


No Slide Title

Хороший стиль программирования
hИмена параметров могут быть включены в прототип функции с целью документирования. Компилятор эти имена игнорирует.


No Slide Title

Типичные ошибки программирования
hОтсутствие точки с запятой в конце прототипа функции.
hОтсутствие прототипа функции, когда функция не определена перед её первым вызовом, приводит к синтаксической ошибке.


No Slide Title

Замечание по технике программирования
Прототип функции, размещенный вне описания какой-то другой функции, относится ко всем вызовам данной функции, появляющимся после этого прототипа в данном файле. Прототип функции, размещенный внутри описания некоторой другой функции, относится только к вызовам внутри этой функции.


No Slide Title

Совет по повышению эффективности
hИспользуйте неформатированный ввод-вывод для достижения максимальной эффективности при обработке файлов большого объёма.


No Slide Title

Типичные ошибки программирования
hПопытка прочитать данные из потока класса ostream (или из любого другого выходного потока).
hПопытка записать в поток класса istream (или в любой другой входной поток).


No Slide Title

Некоторые манипуляторы
setw(int) - устанавливает ширину поля
dec, oct, hex - устанавливают базу преобразования
setbase(int) - устанавливает основание потока
setprecision(int) - устанавливает точность печатаемых
чисел с плавающей запятой setiosflags(long) - устанавливает флаги формата resetiosflags(long) - сбрасывает указанные флаги
setfill(int) - устанавливает значение заполняющего
символа
ws - пропуск символов - разделителей
endl - вставка символа новой строки и очистка потока
flush - очистка потока
ends - вставка конечного нулевого символа в строку


No Slide Title

Совет по повышению эффективности
hЗакрывайте явным образом каждый файл, как только станет понятным, что программа не будет обращаться к этому файлу снова. Это поможет сократить используемые ресурсы, которые программа продолжает потреблять длительное время после того, когда ей уже не надо обращаться к данному файлу. Этот приём делает программу также более ясной.


No Slide Title

hОтсутствие круглых скобок, устанавливающих последовательность выполнения операций, при использовании операций с относительно высоким приоритетом вставить в поток << и взять из потока >>.
#include <iostream.h>
void main()
{int x,y;
cout<<“введите два целых числа”;
cin<<x<<y<<(x==y ? “ ” : “ не ”)
<<“равно”<<y<<endl;
return 0;
}
Типичная ошибка программирования


No Slide Title

Класс памяти
Автоматический
с локальным
временем
жизни
Статический
с глобальным
временем
жизни
auto
register
extern
static


No Slide Title

Лекция № 6
 Не ОО особенности С++,
Потоки ввода-вывода
Уральский государственный университет
Кафедра Математической Физики,
Центр технологий дистанционного образования
Курс Лекций
С++ и Объектно-ориентированное
Программирование
Устинов В.А., Кискачи А.В.


Заметки
Структура

Лекция № 7
Ссылки и
Динамические объекты
Указатели
Типичная ошибка программирования
Хороший стиль программирования
Способы передачи параметров в процедуры
Передача параметров по значению и по ссылке
Типичная ошибка программирования
Замечание по технике программирования
Хороший стиль программирования
Использование ссылок для создания псевдонимов (aliases) переменных
Операторы динамического распределения памяти
Способы использования динамических операций при работе с классами
Динамические объекты
при работе с классами
Типичная ошибка программирования
Хороший стиль программирования
New и Delete в
Конструкторах и Деструкторах
Пример.
Реализация безопасного массива
Пример использования
Типичные ошибки программирования
Термины
Задание на практику


No Slide Title

Термины
•Передача параметров в функцию
•Ссылка
•Указатель
•Псевдоним
•new
•delete


No Slide Title

Задание на практику
•Продемонстрировать два способа использования динамических операций при работе с классами
•Реализовать класс безопасного массива векторов, размерности = 2.
•Продемонстрировать передачу параметров в функции при помощи ссылок.


No Slide Title

Способы передачи параметров в процедуры
В большинстве языков программирования параметры передаются либо по ссылке, либо по значению.
•Подпрограмма работает непосредственно с переменной, переданной ей в качестве параметра.
•Подпрограмме доступна не сама переменная, а только ее значение. Эту переменную нельзя изменить.
Язык По значению По ссылке Пример
Pascal + + Procedure F(x: real);
Procedure F(var x:real);
Cи + - int Sum(int x, int y);
int Gam(int *x, int *y);
C++ + + int Shum(int x, int y);
int Gam(int & x, int & y);


No Slide Title

Передача параметров по значению и по ссылке
#include <iostream.h>
void F1(int x, int y)
{ int z=y; y=x; x=z;}
void F2(int *x, int *y)
{ int z=*y;
*y=*x; *x=z;}
void F3(int & x, int & y)
{int z=y; y=x; x=z;}
void F4(double &x, double &y)
{ int z=y; y=x; x=z;}
указатель
на тип int
ссылка
на тип int
int main()
{
int a=0, b=1;
cout<<a,b; (0)
F1(a,b);
cout<<a,b; (1)
F2(&a, &b);
cout<<a,b; (2)
F3(a,b); cout<<a,b; (3)
F4(a,b); cout<<a,b; (4)
return 0;
}
операция
взятия
адреса
a=0 (0)
b=1
a=0 (1)
b=1 нет
a=1 (2)
b=0 да
a=0 (3)
b=1 да
a=0 (4)
b=1 нет


No Slide Title

Использование ссылок для создания псевдонимов (aliases) переменных
int x=1;
int & xr=x; //ссылка xr становится псевдонимом x
xr=2; //все равно, что x=2
xr++; //то же, что и x++
int x=1;
char & xr=x; /*т.к. типы x и xr не совпадают, ком-
пилятор создает временную пере-
менную типа char для которой xr
будет псевдонимом */
xr=2; //значение x не изменится


No Slide Title

Операторы динамического распределения памяти
new - выделяет блок памяти из кучи
delete - освобождает ранее выделенную память
Три способа использования new
• new type_name float *x=new float;
• new (type_name) float *x=new(float);
• new type_name[expr] float *x=new float[20];
Два способа использования delete
• delete expr float *x=new float[20];delete x;
• delete [expr1] expr float *x=new float[20];delete[20] x;


No Slide Title

Динамические объекты
при работе с классами
class F {
int x,y;
public:
int p;
F(int x1, int y1){x=x1;y=y1;}
void T(void){cout<<x+y+p;}
}
F * W; // Объявление указателя
W = new F(3,6); /* Выделение памяти
и вызов конструктора */
W -> p=2; // Доступ к данным
W -> T( ); // Вызов метода
delete W; // Освобождение памяти
Point *A,*B;
A= new Point(10,30,11);
B= new Circle(15,82,50,7);
…
A -> Show(); B -> Show();
delete A; delete B;


No Slide Title

New и Delete в
Конструкторах и Деструкторах
//Реализация безопасного массива Vect
#include <iostream.h>
#include <stdlib.h> // для exit
class Vect { // интерфейс класса
private:
int *p; int size;
public:
Vect( void ) {size = 10; p = new int [ size ]; }
Vect( int n );
~Vect( void ) { delete [ ] p; }
int & element (int i );
};


No Slide Title

Пример.
Реализация безопасного массива
Vect::Vect ( int n ) //реализация класса vect
{ if ( n <= 1)
{cerr << “неправильный размер массива“ <<
n << endl;
exit(1); }
size = n; p = new int [ size ];
}
int & Vect::element (int i )
{ if ((i < 0 )||( i >= size))
{ cerr << “неправильный индекс массива“ <<
i << endl;
exit(1); }
return ( p[ i ] );
}


No Slide Title

Пример использования
int main( ) {
Vect A, B(5), C(20),
Vect D(- 4);
A.element(1) = 5;
B.element(1) = A.element(1) + 7;
C.element(25) = 0;
cout << A.element(1) << B.element(1);
return 0;
}


No Slide Title

Способы использования динамических операций при работе с классами
Динамическое создание переменных класса
(экземпляров объектов)
Figure * X; X = new Figure; … delete X;
•гибкость и контроль пользователя за памятью при работе с готовыми объектами
Использование операций динамического распре-деления памяти в конструкторах и деструкторах
class Figure {
float * X;
Figure (int n) { X = new Float[n] }; … }
•сам объект определяет свою потребность в памяти


No Slide Title

Указатели
Указатели - это переменные, которые содержат
в качестве своих значений адреса памяти.
int *countPtr, count;
Указатель должен быть инициализирован либо при объявлении, либо при помощи оператора присваивания.
countPtr=0; countPtr=NULL; count=5; countPtr=&count;
Операция * - операция косвенной адресации или операция разыменования, возвращает значение объекта на который указывает операнд.
Операция & - возвращает адрес своего операнда.
Операции & и * - дополняют друг друга.


No Slide Title

Типичная ошибка программирования
hОперация * косвенной адресации не распространяется на все имена переменных в объявлении. Каждый указатель должен быть объявлен с помощью символа *, стоящего перед именем.


No Slide Title

Хороший стиль программирования
hХотя это и не обязательно, включаите буквы Ptr в имена переменных указателей, чтобы было ясно, что эти переменные являются указателями и требуют соответствующей обработки.
hПрисваивайте начальные значения указателям во избежание неожиданных результатов.


No Slide Title

Типичная ошибка программирования
hНе разыменовывается указатель, когда это необходимо сделать, чтобы получить значение, на которое указывает этот указатель.


No Slide Title

Замечание по технике программирования
При использовании вызова по значению в вызываемой функции можно изменять только одно значение. Это значение должно быть задано как возвращаемое в операторе return. Для модификации множества значений в вызываемой функции нужно использовать передачу параметров по ссылке.


No Slide Title

Хороший стиль программирования
hПрежде, чем использовать функцию, проверьте прототип функции, чтобы определить, имеет ли она возможность модифицировать передаваемые ей значения.


No Slide Title

Хороший стиль программирования
hИспользуя операцию new проверьте, не вернула ли она нулевой указатель. Выполните соответствующую обработку ошибки, если операция new не выделила область памяти.
hКогда память, которая динамически выделена операцией new, более не требуется, используйте операцию delete для немедленного освобождения памяти.


No Slide Title

Типичная ошибка программирования
hНе осуществляется возвращение динамически выделенной памяти, когда эта память уже более не требуется. Это может явится причиной преждевременного переполнения памяти. Иногда это явление называют “утечкой памяти”.


No Slide Title

Типичные ошибки программирования
hОсвобождение операцией delete памяти, которая не была выделена динамически операцией new.
hСсылка на область памяти, которая была освобождена.


No Slide Title

Лекция № 7
 Ссылки и
Динамические объекты
Уральский государственный университет
Кафедра Математической Физики,
Центр технологий дистанционного образования
Курс Лекций
С++ и Объектно-ориентированное
Программирование
Устинов В.А., Кискачи А.В.


Заметки
Структура
Лекция № 8
Перегрузка операций и
дружественные функции
Определения
Особенности перегрузки операторов
Особенности перегрузки операторов
Особенности перегрузки операторов
Типичные ошибки программирования
Замечание по технике программирования
Хороший стиль программирования
Использование указателя this
Оператор как метод класса
Оператор как дружественная функция
Метод класса или дружественная функция?
Хороший стиль программирования
Совет по повышению эффективности
Перегрузка операций << и >>
Слайд 16
Слайд 17
Пример : Класс Vector
Пример : Класс Vector (2)
Пример : Класс Vector (3)
Пример : Класс Vector (4)
Пример : Класс Vector (5)
Термины
Задание на практику


No Slide Title

Термины
•Operator
•Friend
•This
•Аргументы операторов
•2 способа перегрузки


No Slide Title

Задание на практику
•Создать класс Fraction (a / b), осуществляюший с целыми дробями операции +, - , *, / , <<.
•Продемонстрировать работу этого класса.
•Использовать закрытый метод redu, возвращающий дробь с наименьшим общим знаменателем.
Fraction Fraction::redu( ) {
int g =1; int min =a;
if (a > b) { min =b; }
for (int
i =1;
i<=min;
i++)
{ if ( (a % i == 0) && (b % i == 0)) {g =i;} }
a = a /g; b = b /g;
return * this;
}


No Slide Title

Определения
•Каждый, определенный в С++ оператор (“op”) соответствует функции с именем operator op и требуемым числом и типами аргументов.
•Большинство, существующих в языке C++ операторов, может быть переопределено (перегружено) для других типов аргументов.
Перегрузка оператора, принимающего один параметр:
return_type operator op(parameter_type parameter)
{ операторы; }
Перегрузка оператора, принимающего два параметра:
return_type operator op(parameter_type1 parameter_L,
parameter_type2 parameter_R)
{ операторы; }
Синтаксис


No Slide Title

Оператор как метод класса
class Complex {
double Re, Im;
public:
Complex(double X=0, double Y=0) { Re=X; Im=Y};
Complex operator + (Complex Z)
{
Complex tmp= * this;  tmp.Re+=Z.Re;
tmp.Im+=Z.Im;
return tmp; }
};
Complex X(0,1), Y(1,0), Z;
Z=X + Y; // интерпретируется как Z = X.operator+(Y);
•При использовании оператора как метода класса сам объект считается первым аргументом оператора.


No Slide Title

Пример : Класс Vector
#include <iostream.h>
class Vector {
int X, Y; // Компоненты (координаты) вектора
friend Vector & operator --(Vector &); /* Оператор
уменьшения. Вычитает единичный вектор и
возвращает сам вектор */
friend Vector operator- (const Vector &,const Vector &);
/*Разность двух векторов.
Возвращается не ссылка.*/
public:
//Присвоить значения компонентам
void Assign (int x1, int y1) { X=x1; Y=y1;}
Vector operator ++ ( ); /* Прибавляет к вектору
единичный вектор и возвращает сам вектор */


No Slide Title

Пример : Класс Vector (2)
Vector operator ++ ( int ); /*Постфиксный оператор
увеличения. Возвращает ЗНАЧЕНИЕ вектора
до увеличения */
Vector operator + (const Vector & ); /*Найти
сумму 2-х векторов. Возвращается НЕ ССЫЛКА */
void print ( )
//Напечатать значения компонент вектора
{ cout <<“Координаты вектора“ <<X<<Y<<“\n”;}
}; // Конец описания класса
Vector & Vector::operator ++( )
{ X++; Y++; /* this - указатель на объект для
которого данный метод вызывается */
return *this; }


No Slide Title

Пример : Класс Vector (3)
Vector Vector::operator ++ ( int ) {
// аргумент игнорируется
Vector vec_tmp =*this;
X++; Y++;
return vec_tmp; }
Vector Vector::operator + (const Vector & vec)
{ Vector vec_tmp =*this; /*создать временный
вектор и присвоить ему значение ПЕРВОГО
слагаемого */
// сложить:
vec_tmp.X+=vec.X;
vec_tmp.Y+=vec.Y;
return vec_tmp; } //возвратить результат


No Slide Title

Пример : Класс Vector (4)
Vector & operator -- ( Vector & vec ) {
vec.X--; vec.Y--;
return vec; } /* возвращает сам объект
после уменьшения */
Vector operator - (const Vector & vec1,
const Vector & vec2)
{ Vector vec_tmp =vec1;
vec_tmp.X -=vec2.X; vec_tmp.Y -=vec2.Y;
return vec_tmp; } /* возвращает результат
вычитания векторов */
const NV =5;
int main ( ) /*создать массив
{Vector VA[NV], v1; векторов и еще один вектор*/


No Slide Title

Пример : Класс Vector (5)
for ( int i =0; i< NV; i++)
{VA[ i ].Assign( i, i+1);
cout <<“Присвоили значение вектору“ << i <<“\n“;
VA[ i ].print( ); }
for ( i =0; i<NV; i++) /*компилятор интерпретирует
{ --VA[ i ]; как operator--(VA[i ]) */
cout <<“Вычли единичный вектор из вектора“ << i
<< “\n“;
VA[i].print( ); }
v1.Assign(0,0);
cout<<“Cумма всех векторов массива :“;
for (i =0; i<NV; i++) { v1 =v1+VA[ i ]; }
/*интерпретируется как v1.operator+(VA[i]) */
v1.print( );
return 0;
}


No Slide Title

Особенности перегрузки операторов
•Операция, подлежащая переопределению должна присутствовать в языке (нельзя определить # )
•Старшинство операций и ассоциативность не могут быть изменены перегрузкой. Приоритет можно изменить принудительно при помощи скобок.
•Нельзя переопределить операции: . * :: ?:
•Нельзя создавать новые операции.
•Нельзя изменить количество операндов, которые берет операция: унарные операции остаются унарными, бинарные - бинарными. Единственная тернарная операция в C++ не может быть перегружена.


No Slide Title

Оператор как дружественная функция
•friend дает возможность функциям, не принадлежа-щим классу, для доступа к элементам данного класса
сlass Complex {
double Re, Im;
public:
Complex (double X=0, double Y=0) { Re=X; Im=Y};
friend Complex operator - (Complex Z, Complex W);
};
Complex operator- (Complex Z, Complex W)
 { Complex tmp;
tmp.Re=Z.Re-W.Re;
tmp.Im=Z.Im-W.Im;
return tmp; };
Complex X(0,1), Y(1,0), Z;
Z=X + Y; // интерпретируется как Z = operator+(X,Y);


No Slide Title

•Если возможно, помещайте перегруженный оператор в класс. Это позволит облегчить чтение программы и изменить поведение перегруженного оператора при наследовании от класса.
•Оператор не может быть включен в класс, если его левый аргумент не является объектом данного класса.
•При перегрузке операторов = , [ ] или ( ) функция перегрузки операции должна быть объявлена как элемент класса.
•Функция-операция, реализованная не как функция-элемент, должна быть другом, если эта функция должна иметь прямой доступ к закрытым или защищенным элементам этого класса.
•Для сохранения коммутативности в перегруженых операторах лучше всего использовать глобальную перегрузку (т.е. Дружественные функции)
Метод класса или дружественная функция?


No Slide Title

Особенности перегрузки операторов
•При работе с классами оператор может быть
¨членом (методом) класса;
¨дружественной функцией для данного класса (не методом данного класса).
•Компилятор различает префиксные и постфиксные формы ++ и --.
++d генерирует вызов функции d.operator++() с прототипом Date operator++() или operator++(d) с прототипом friend Date operator++(Date &) .
d++ генерирует вызов функции d.operator++(0) с прототипом Date operator++(int) или operator++(d,0) с прототипом friend Date operator++(Date &, int)


No Slide Title

Особенности перегрузки операторов
hНельзя изменить с помощью перегрузки операции смысл работы операции с объектом встроенного типа. Перегрузка операций применима только для работы с объектами типов, определенных пользователем, или со смесью объектов.
hНе существует неявной перегрузки. Ошибочно предположение, что перегрузка + автоматически перегружает +=.
h
int operator + (int x, int y) { return(x+y+1) }; // пример
Сколько будет 2 + 2 ? ?????????


No Slide Title

Перегрузка операций << и >>
•Операции << и >> можно перегрузить для того, чтобы выполнять ввод и вывод типов, определенных пользователем.
#include <iostream.h>
class PhonNum {
friend ostream & operator <<(ostream &,const PhonNum &);
friend istream & operator >>(istream &,const PhonNum &);
private:
char areaCode[4]; /* 3-хцифровой код местности и нулевой символ*/ char exchange[4]; /* 3-хцифровой коммутатор и нулевой символ*/
char line[5]; /* 4-хцифровая линия и нулевой символ*/
};


No Slide Title

Типичные ошибки программирования
hПопытка перегрузить операцию, запрещенную для перегрузки.
hПопытка создавать новые операции.
hПопытка изменить работу операции с объектами встроенного типа.
hПредположение, что перегрузка операции (такой как +) автоматически перегружает связанные с ней операции (такие как +=). Операции можно перегружать только явно; неявной перегрузки не существует.


No Slide Title

Замечание по технике программирования
По меньшей мере один аргумент функции -операции должен быть объектом класса или ссылкой на объект класса. Это предохраняет программиста от изменения работы операции с объектом встроенного типа.


No Slide Title

Хороший стиль программирования
hЧтобы обеспечить согласованность связанных операций рекомендуется использовать одни из них для реализации других (т.е. используйте перегруженную операцию + для реализации перегруженной операции +=).


No Slide Title

Совет по повышению эффективности
hМожно было бы перегружать операцию не как элемент и не как дружественную функцию, но такая функция, нуждающаяся в доступе к закрытым или защищенным данным класса, потребовала бы использования функций set или get, предусмотренных открытым интерфейсом этого класса. Накладные расходы на вызовы этих функции могли бы вызвать ухудшение производительности


No Slide Title

Хороший стиль программирования
hПри перегрузке унарных операций предпочтительнее создавать функции-операции, являющиеся элементами класса, вместо дружественных функций, не являющихся элементами. Дружественных функций и дружественных классов лучше избегать до тех пор, пока они не станут абсолютно необходимыми. Использование друзей нарушает инкапсуляцию класса.


No Slide Title

ostream & operator <<(ostream &output,
const PhonNum &num)
{ output << “(“<< num.areaCode<<“) ”<<num.exchange <<“-”
<< num.line;
return output; // разрешает cout <<a <<b <<c;
}
istream & operator >>(istream &input, const PhonNum &num)
{ input.ignor(); // пропуск (
input.getline (num.areaCode, 4); //ввод кода местности
input.ignor( 2); // пропуск ) и пробела
input.getline (num.exchange, 4); //ввод коммутатора
input.ignor(); // пропуск дефиса
input.getline (num.line, 5); //ввод линии
return input; // разрешает cin >> a>> b >>c;
}


No Slide Title

main()
{ PhoneNum phone;
cout << “ Введите номер телефона в виде”
<< “ (???) ???-????:” <<endl;
cin >>phone;
cout << phone;
}
•Когда компилятор видит выражение cin >>phone в main, он генерирует вызов функции
operator >>(cin,phone);
•После выполнения этого вызова параметр input становится псевдонимом для cin, а параметр num становится псевдонимом для phone.
•Когда компилятор видит выражение cout <<phone в main, он генерирует вызов функции
operator <<(cout,phone);


No Slide Title

Использование указателя this
Каждый объект сопровождается указателем на самого себя - называемым указателем this - это неявный аргумент во всех ссылках на элементы внутри этого объекта.Указатель this можно использовать также и явно. Каждый объект может определить свой собственный адрес с помощью ключевого слова this. Указатель this неявно используется для ссылки как на данные-элементы, так и на функции-элементы объекта. Тип указателя this зависит от типа объекта.
this


No Slide Title

Лекция № 8
 Перегрузка операций и
дружественные функции
Уральский государственный университет
Кафедра Математической Физики,
Центр технологий дистанционного образования
Курс Лекций
С++ и Объектно-ориентированное
Программирование
Устинов В.А., Кискачи А.В.


‹верхний колонтитул›
‹дата/время›
Click to edit Master text styles
Second level
Third level
Fourth level
Fifth level
‹нижний колонтитул›
‹#›
1. Упор на алгоритме. Языки поддерживают эту парадигму, предоставляя средства для передачи аргументов функциям и возврата значений из функции.
2.С течением времени акцент при разработке программ сместился от проектирования процедур в сторону организации данных. Помимо прочего, это явилось отражением факта увеличения размеров программ. Набор связанных процедур вместе с данными, которые они обрабатывают, часто называют модулем. Техника проектирования “хороших процедур” теперь применяется для каждой процедуры в модуле.
3. Модульное программирование приводит к централизации управления данными определенного типа в отдельном модуле. Однако это решение далеко от идеала, т.к., к примеру, такие типы (реализованные посредством модуля и предоставляющие доступ к реализации) имеют меньшую по сравнению со встроенными типами поддержку. Там где нужно более одного объекта определенного типа имеет смысл непосредственно определять типы, которые ведут себя (почти) также как встроенные.
4.Тому кто захочет использовать стек, вряд ли всегда будет нужен именно стек символов. Стек – более общее понятие, которое не зависит от определения символа. Следовательно имеет смысл его представить независимо. Вообще, если алгоритм можно выразить независимо от деталей представления и это можно сделать приемлемым (с точки зрения накладных расходов) способом без логических искажений, то так и нужно поступать.
ПАРАДИГМЫ, ПРИВЕДЕННЫЕ ЗДЕСЬ, ИМЕЮТ ТЕНДЕНЦИЮ ВЗАИМНО ДОПОЛНЯТЬ И ПОДДЕРЖИВАТЬ ДРУГ ДРУГА. ОПЫТНЫЙ РАЗРАБОТЧИК ПРИМЕНЯЕТ РАЗЛИЧНЫЕ ПАРАДИГМЫ ПО МЕРЕ НАДОБНОСТИ.
Многие вычисления подразумевают создание наборов объектов в различных формах и обработку таких наборов. Класс, главной целью которого является хранение объектов, называется контейнером. Стандартные контейнеры представлены в заголовочных файлах <vector>, <list>, <map>. Стандартные контейнеры и их основные операции разрабатывались так, чтобы обеспечить единство записи и обозначений. Смысл одноименных операций одинаков для различных контейнеров. Например, каждый контейнер имеет функцию size(), которая возвращает количество элементов в нем. Единство обозначений помогает программистам создавать новые типы, которые можно использовать как стандартные. Объекты редко просто хранятся в контейнере. Они сортируются, печатаются, удаляются, ищутся вхождения элементов и т.д. Поэтому в стандартной библиотеке есть еще и общие алгоритмы их обработки.
Что такое алгоритм? Вот общее определение алгоритма:”конечный набор правил, который определяет последовательность операций для решения конкретного множества задач и обладает пятью важными чертами: Конечность… Определенность… Ввод… Вывод… Эффективность” [Knuth, 1968]. Объекты редко просто хранятся в контейнере. Они сортируются, печатаются, удаляются; извлекаются подмножества элементов контейнера, ищутся вхождения элементов и т.д. Поэтому в стандартной библиотеке, наряду с самыми общеупотребительными контейнерами, имеются также и общие алгоритмы их обработки. В контексте стандартной библиотеки С++ алгоритм – это набор шаблонов, работающих с последовательностью элементов. Представлены алгоритмы в заголовочном файле <algorithm>. Эти и многие другие алгоритмы можно применять к контейнерам, строкам string и встроенным массивам.
Лучшие примеры итераторов:) begin(), end() (итераторы, указывающие на начало и конец). Итераторы возвращаются некоторыми алгоритмами. Например, стандартный алгоритм find ищет значение в последовательности, и возвращает итератор, указывающий на найденный элемент. Общее для всех итераторов – смысл и имена операций. Например, применение ++ к любому итератору приведет к тому, что итератор будет указывать на следующий элемент. Аналогично, *означает элемент, на который ссылается итератор. В действительности любой объект, подчиняющийся нескольким простым правилам, наподобие перечисленных, является итератором. Более того пользователю редко требуется знать тип конкретного итератора. Каждый контейнер “знает” тип своего итератора и обеспечивает доступ к нему через стандартные имена iterator и const_iterator…


Заметки
Структура

Лекция № 9
Шаблоны
Эволюция парадигм программирования
Шаблоны функций
Пример шаблона функции
Особенности использования шаблонов
Перегрузка шаблонных функций
Пример перегрузки шаблона функции
Шаблоны классов
Замечание по технике программирования
Зачем нужны Шаблоны
Связный список объектов любого типа
Определение функций-членов для Шаблонов
Особенности использования указателей для Шаблонов
Шаблоны и друзья.
Как избежать сложностей
с Шаблонами
Типичная ошибка программирования
Классы контейнеры
Стандартные контейнеры.
Алгоритмы стандартной библиотеки.
Итераторы.
Итераторы.
Термины
Задание на практику


No Slide Title

Термины
•Шаблон функции
•Шаблон класса
•Шаблонная функция
•Шаблонный класс
•Параметр типа


No Slide Title

Задание на практику
•Создать шаблон класса - очередь размером size на основе массива queue[size].
•Реализовать Конструктор, присваивающий начальные значения индексу вершины и хвоста очереди.
•Методы помещения и извлечения значений из очереди с контролем пустоты или полноты очереди.
•Продемонстрировать работу программы для целых и вещественных чисел и символов (char).
•


No Slide Title

Связный список объектов любого типа
template <class T>
class Spisok {
public:
T GetData( );
Spisok * GetNext( );
void SetData(T val);
private:
T Data;
Spisok * Next;
};
Класс Spisok
можно использовать
как для создания списка целых чисел так для вещественных и других типов
Spisok < int > List_int;
Spisok < float > List_float;
Spisok < double > List_double;


No Slide Title

Зачем нужны Шаблоны
//связный список
целых чисел
class SP_Int {
public:
int GetData( );
SP_Int * GetNext( );
void SetData (int val);
private:
int Data;
SP_Int * Next;
};
Как сконструировать класс, реализующий связный список для любых типов данных ?
//связный список
вещественных чисел
class SP_Flo {
public:
float GetData( );
SP_Flo * GetNext( );
void SetData (float val);
private:
float Data;
SP_Flo * Next;
};


No Slide Title

Определение функций-членов для Шаблонов
•При определении функций-членов для шаблонов также необходимо использовать template
•
template < class T >
T Spisok < T > :: GetData( ) {return Data; }
template < class T >
void Spisok < T > :: SetData (T val ) {Data = val; }
Использование классов-шаблонов
•Для использования класса-шаблона необходимо указать нужный тип в угловых скобках:
Spisok < int > sp1; //генерация списка целых чисел
//динамическое создание списка веществен. чисел
Spisok < float > * sp2; sp2 = new Spisok < float >;


No Slide Title

Шаблоны классов
•Шаблоны позволяют создавать универсальный фрагмент кода , а затем использовать его многократно, создавая классы, являющиеся специфическими версиями родового класса для конкретного типа данных.
•Шаблоны классов также называют параметризованными типами.
•При проектировании шаблонного класса определите, какие типы данных необходимо сделать родовыми.
•Предварите объявление класса словом template, за которым укажите список родовых типов, заключаемый в угловые скобки:
template < class тип 1, class тип 2 … >
• Затем определите класс обычным образом, используя родовые типы.


No Slide Title

Особенности использования указателей для Шаблонов
•При объявлении класса template < class T >
не используйте угловые class List {
скобки < > при описании public:
указателей на класс List * Next;
List * GetNext( );
};
•При определении функций-членов необходимо использовать < > при описании указателя на класс
template < class T >
List < T > * List<T>::GetNext( ) { return Next; }
•
•При создании указателей на шаблонный класс, необходимо использовать угловые скобки
List < int > * spisok1;


No Slide Title

Как избежать сложностей
с Шаблонами
•Можно определить новый тип для тех видов шаблонов, экземпляры которых необходимо создавать в конкретной программе.
typedef Spisok < float > SP_Float;
typedef Spisok < int > SP_Int;
далее использовать новые типы стандартным образом.
•Для использования классов-шаблонов в более чем одном файле, необходимо включить объявление шаблонного класса и определение всех его методов в заголовочный файл.


No Slide Title

Шаблоны функций
•Определяют базовый набор операций, применя-емых к разным типам данных.
•Оперируют с тем типом данных, которые получа-ют в качестве параметра.
•Компилятор автоматически генерирует объектные коды функций, обрабатывающих каждый тип данных.
•Типовая форма определения родовой функции или функции-шаблона имеет вид:
template <class T1, class T2, … >
возвращаемое_значение имя_функции
( список параметров )
{ тело функции }
Каждый формальный тип параметра предваряется ключевым словом class.


No Slide Title

Пример шаблона функции
#include <iostream.h>
template < class X >
void swap(X & a, X & b)
{ X tm;
tm =a; a =b; b =tm; }
void main( )
{ int i=10, j=20; float x=10.1, y=23.3;
cout << "Исх. Знач. i, j = " << i << ' ' << j <<endl;
cout << "Исх. Знач. x, y = " <<x<<' '<< y <<endl;
swap(i, j); // обмен целыми
swap(x, y); // обмен действительными
cout<<"Новые знач. i, j = "<< i << ' '<< j << endl;
cout << "Новые знач. x, y = " << x << ' ' << y << endl;
}


No Slide Title

Особенности использования шаблонов
•Каждый формальный параметр в определении шаблона функции должен появиться в списке параметров функции по крайней мере один раз.
•Имя формального параметра может использоваться в списке параметров заголовка шаблона только один раз.
•Одно и то же имя формального параметра шаблона функции может быть использовано несколькими шаблонами.
•Создание программой слишком большого числа копий может потребовать значительных ресурсов памяти.


No Slide Title

Перегрузка шаблонных функций
Все родственные функции, полученные из шаблона имеют одно и то же имя; поэтому компилятор использует механизм перегрузки для вызова соответствующей функции.
Сам шаблон можно переопределить двумя способами:
1.Определив другие шаблоны, имеющие то же самое имя функции, но другие параметры.
2. Создав другую не шаблонную функцию, с тем же самым именем, но другим набором параметров функции.


No Slide Title

Замечание по технике программирования
Применение шаблонов классов увеличивает возможности повторного использования программного обеспечения, когда классы для конкретного типа данных могут создаваться на основе родовой версии класса.


No Slide Title

Типичная ошибка программирования
hВ отличие от не шаблонных классов, которые могут быть вложенными, шаблоны классов не могут вкладываться друг в друга. Попытка вложить один шаблон класса внутрь другого приводит к синтаксической ощибке.


No Slide Title

Эволюция парадигм программирования
Процедурное программирование
Реши, какие требуются процедуры; используй наилучшие доступные алгоритмы.
Модульное программирование
Реши, какие требуются модули; разбей программу так, чтобы скрыть данные в модулях.
Объектно-ориентированное программирование
Реши, какие требуются классы; обеспечь полный набор операций для каждого класса; явно вырази общность через наследование.
Обобщенное программирование
Реши, какие требуются алгоритмы;параметризуй их так, чтобы они могли работать со множеством подходящих типов и структур данных.
*
1. Упор на алгоритме. Языки поддерживают эту парадигму, предоставляя средства для передачи аргументов функциям и возврата значений из функции.
2.С течением времени акцент при разработке программ сместился от проектирования процедур в сторону организации данных. Помимо прочего, это явилось отражением факта увеличения размеров программ. Набор связанных процедур вместе с данными, которые они обрабатывают, часто называют модулем. Техника проектирования “хороших процедур” теперь применяется для каждой процедуры в модуле.
3. Модульное программирование приводит к централизации управления данными определенного типа в отдельном модуле. Однако это решение далеко от идеала, т.к., к примеру, такие типы (реализованные посредством модуля и предоставляющие доступ к реализации) имеют меньшую по сравнению со встроенными типами поддержку. Там где нужно более одного объекта определенного типа имеет смысл непосредственно определять типы, которые ведут себя (почти) также как встроенные.
4.Тому кто захочет использовать стек, вряд ли всегда будет нужен именно стек символов. Стек – более общее понятие, которое не зависит от определения символа. Следовательно имеет смысл его представить независимо. Вообще, если алгоритм можно выразить независимо от деталей представления и это можно сделать приемлемым (с точки зрения накладных расходов) способом без логических искажений, то так и нужно поступать.
ПАРАДИГМЫ, ПРИВЕДЕННЫЕ ЗДЕСЬ, ИМЕЮТ ТЕНДЕНЦИЮ ВЗАИМНО ДОПОЛНЯТЬ И ПОДДЕРЖИВАТЬ ДРУГ ДРУГА. ОПЫТНЫЙ РАЗРАБОТЧИК ПРИМЕНЯЕТ РАЗЛИЧНЫЕ ПАРАДИГМЫ ПО МЕРЕ НАДОБНОСТИ.


No Slide Title

Пример перегрузки шаблона функции
template <class T> T sqrt (T);
template <class T> complex<T> sqrt (complex<T>);
double sqrt (double);
void f ( complex<double> z )
{ sqrt (2); // sqrt<int> (int)
sqrt (2.0); // sqrt (double)
sqrt (z); // sqrt <double> (complex<double>)
}
Сначала компилятор ищет функцию, которая точно соответствует имени и типам параметров, затем шаблон для генерации, затем выполняется последовательный подбор перегруженной функции. Если подходящая функция не найдена, то генерируется сообщение об ошибке.


No Slide Title

Шаблоны и друзья.
template <class T> class X
{ …
friend void f1 ( );
// дружит с любым шаблонным классом
friend void f2 ( X<T> &);
// для каждого шаблонного класса своя
friend void A:: f3 ( );
friend void C<T>:: f4 ( X<T> &);
friend class Y;
// каждая из функций-элементов класса Y
// будет дружественной для всех
// произведенных шаблонных классов
friend class Z<T>;


No Slide Title

Классы контейнеры
К наиболее популярным типам классов относятся классы контейнеры, т.е. классы, спроектированные для хранения в них совокупностей объектов.
Классы контейнеры обычно снабжены такими возможностями, как вставка, удаление, поиск, сортировка, проверка наличия элемента в классе и тому подобное.
Массивы, стеки, очереди, связные списки - всё это примеры классов контейнеров.


No Slide Title

Стандартные контейнеры.
vector<T> - вектор переменного размера
list<T> - двусвязный список
queue<T> - очередь
stack<T> - стек
deque<T> - очередь с двумя границами
priority_queue<T> - очередь, отсортированная по
значению
set<T> - множество
multiset<T> - множество, в котором одно значение
можно встретить несколько раз
map<key,val> - ассоциативный массив
multimap< key,val> - ассоциативный массив, в котором
ключ (key) может встречаться
несколько раз
< vector > < list > < map >
*
Многие вычисления подразумевают создание наборов объектов в различных формах и обработку таких наборов. Класс, главной целью которого является хранение объектов, называется контейнером.
Стандартные контейнеры представлены в заголовочных файлах <vector>, <list>, <map>. Стандартные контейнеры и их основные операции разрабатывались так, чтобы обеспечить единство записи и обозначений. Смысл одноименных операций одинаков для различных контейнеров. Например, каждый контейнер имеет функцию size(), которая возвращает количество элементов в нем. Единство обозначений помогает программистам создавать новые типы, которые можно использовать как стандартные.
Объекты редко просто хранятся в контейнере. Они сортируются, печатаются, удаляются, ищутся вхождения элементов и т.д. Поэтому в стандартной библиотеке есть еще и общие алгоритмы их обработки.


No Slide Title

Алгоритмы стандартной библиотеки.
for_Each() вызвать функцию для каждого элемента
find() найти первое вхождение аргументов
find_if() найти первое соответствие предикату
count() сосчитать число вхождений элемента
count_if() сосчитать число соответствий предикату
replace() заменить элемент новым значением
copy() скопировать элементы
copy_unique() скопировать только различные элементы
sort() отсортировать элементы
equal_range() найти диапазон всех элементов с
одинаковыми значениями
merge() слияние отсортированных
последовательностей
<algorithm>
*
Что такое алгоритм? Вот общее определение алгоритма:”конечный набор правил, который определяет последовательность операций для решения конкретного множества задач и обладает пятью важными чертами: Конечность… Определенность… Ввод… Вывод… Эффективность” [Knuth, 1968].
Объекты редко просто хранятся в контейнере. Они сортируются, печатаются, удаляются; извлекаются подмножества элементов контейнера, ищутся вхождения элементов и т.д. Поэтому в стандартной библиотеке, наряду с самыми общеупотребительными контейнерами, имеются также и общие алгоритмы их обработки.
В контексте стандартной библиотеки С++ алгоритм – это набор шаблонов, работающих с последовательностью элементов. Представлены алгоритмы в заголовочном файле <algorithm>.
Эти и многие другие алгоритмы можно применять к контейнерам, строкам string и встроенным массивам.


No Slide Title

Итераторы.
Каждый конкретный итератор - объект некоторого
типа, содержащий информацию, необходимую для
работы с контейнером определенного типа.
int count(const string & s, char c)
{ string::const_iterator i=find(s.begin(),s.end(),c);
int n=0;
while (i!=s.end()) {
++n;
i=find(i+1,s.end(),c); }
return n;
}
*
Лучшие примеры итераторов:) begin(), end() (итераторы, указывающие на начало и конец). Итераторы возвращаются некоторыми алгоритмами. Например, стандартный алгоритм find ищет значение в последовательности, и возвращает итератор, указывающий на найденный элемент.
Общее для всех итераторов – смысл и имена операций. Например, применение ++ к любому итератору приведет к тому, что итератор будет указывать на следующий элемент. Аналогично, *означает элемент, на который ссылается итератор. В действительности любой объект, подчиняющийся нескольким простым правилам, наподобие перечисленных, является итератором. Более того пользователю редко требуется знать тип конкретного итератора. Каждый контейнер “знает” тип своего итератора и обеспечивает доступ к нему через стандартные имена iterator и const_iterator…


No Slide Title

Итераторы.
find() работает аналогично с любым стандартным контейнером, поэтому и count() может быть написана в общем виде.
template<class C, class T>
int count(const C & v, T val)
{ typename C::const_iterator
i=find(v.begin(),v.end(),val);
int n=0;
while (i!=v.end()) {
++n;
++I;
i=find(i,v.end(),val); }
return n;
}


No Slide Title

Лекция № 9
Шаблоны
Уральский государственный университет
Кафедра Математической Физики,
Центр технологий дистанционного образования
Курс Лекций
С++ и Объектно-ориентированное
Программирование
Устинов В.А., Кискачи А.В.