329.93K
Category: programmingprogramming

Объектно-ориентированный подход

1.

Объектно-ориентированный
подход (программирование)
Объектно-ориентированная
технология
программирования основывается на так
называемой объектной модели.
Основными ее принципами являются:
абстракция;
инкапсуляция;
наследование;
полиморфизм.
Каждый из этих принципов сам по себе
не нов, но в объектной модели они
впервые применены в совокупности.

2.

Абстракция – совокупность существенных характеристик некоторого объекта,
которые отличают его от всех других
объектов.
Абстракция – способ выделения самых
значимых характеристик объекта, при этом
менее значимые отбрасываются.
Абстракция – свойство, позволяющее
создавать новые типы данных, выделяя
общие абстрактные свойства этих типов.

3.

Инкапсуляция – механизм, позволяющий
объединять
все
свойства
объекта,
определяющих его состояние и поведение,
в единую абстракцию и задавать
ограничение доступа к реализации этих
свойств.
Инкапсуляция

скрытие
деталей
реализации; объединение данных и
действий над ними.
В языке С++ инкапсуляция данных
реализуется с помощью механизма
классов.

4.

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

5.

Наследование – механизм, позволяющий строить
иерархию типов.
Это предполагает определение базового типа
(или прародителя), а затем использование этого
типа для построения производных типов
(потомков).
Причем каждый производный тип наследует все
свойства базового типа, включая как данные, так
и набор операций (поведение).
Новые типы данных могут также иметь свои
дополнительные свойства.
Таким образом, они не дублируют свойства
базового класса, а только имеют возможность их
использовать.

6.

Полиморфизм
(многообразие
форм),
возможность определения единого по имени
действия, применимого ко всем объектам
иерархии, причем каждый объект реализует это
действие собственным способом.
К формам полиморфизма в С++ относятся:
перегрузка функций и операций;
виртуальные функции;
шаблоны – обобщение функций.
Полиморфизм позволяет повысить процент
повторного использования кода и сократить тем
самым размер программы и временные затраты на
её написание.
Класс (объект) – инкапсулированная абстракция с
четким протоколом доступа.

7.

Основные понятия в ООП – объект и сообщение.
Разрабатываемая программа состоит из программных
объектов, которые взаимодействуют через передачу
сообщений.
Объект моделирует сущность реального мира или
некоторое абстрактное понятие.
Объекты характеризуются:
состоянием , поведением.
Состояние объекта характеризуется набором
конкретных значений его возможных свойств (напр.:
вес, цвет, размер, прочность, форма и т.д.).
Поведение определяется совокупностью методов,
обеспечивающих передачу сообщений между
объектами – то как объект проявляет себя во внешнем
мире.

8.

Объектно-ориентированное
программирование
(ООП)
предоставляет
возможность
создавать
объекты, которые соединяют свойства и поведения в
самостоятельный союз, который затем можно
многоразово использовать. Это приводит к созданию
следующего кода:
you.driveTo(work);
Так не только читабельнее, но и понятнее, кем
является объект (you – в) и какое поведение
вызывается (driveTo – поездка).
Вместо сосредоточения на написании функций, мы
концентрируемся на определении объектов, которые
имеют четкий набор поведений.
Поэтому эта парадигма называется «объектноориентированной».

9.

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

10.

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

11.

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

12.

Начало ООП: впервые в языке Simula,
далее Smalltalk, Object Pascal и C++.
Общая тенденция в развитии языков
программирования:
смещение
акцентов
от
программирования отдельных деталей к
программированию
более
крупных
компонент;
развитие и совершенствование языков
программирования высокого уровня.

13.

Сообщение определяет способы взаимодействия
объектов.
Сообщение определяется своим именем и всегда
имеет адресата – кому оно адресовано. Кроме того,
сообщение может иметь дополнительные аргументы,
передаваемые адресату.
Например:
Объект Set – множество (математическое понятие).
Множество определяется мощностью (количеством
разных элементов) и значениями элементов
множества – это и есть состояние объекта.
Создавая конкретные множества, мы создаем
индивидуальные экземпляры данных (свойство
индивидуальности), каждый из которых имеет
собственные (возможно, и одинаковые) значения.

14.

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

зависимости от конкретного сообщения).

15.

Рассмотрим тот же объект множество. Пусть
aSet – конкретный экземпляр множества.
Тогда посылку сообщений данному экземпляру
можно проиллюстрировать:
aSet.Include(5); aSet.Print();
адресат
сообщение
дополнительные
параметры
aSet
Включи в себя
значение
aSet
Покажи себя

16.

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

17.

Технология разработки ОО программ
Этап – разработка иерархии классов:
1. В предметной области выделяются понятия, которые
можно использовать как классы.
2. Определяются операции над классами, которые
впоследствии станут методами класса. Их можно
разбить на группы:
- связанные с конструированием и копированием
объектов;
- для поддержки связей между классами, которые
существуют в прикладной области;
- позволяющие представить работу с объектами в
удобном виде.
3. Определяются функции, которые будут
виртуальными.
4. Определяются зависимости между классами.
Процесс создания иерархии классов – итерационный.
Например, можно в двух классах выделить общую часть в
базовый класс и сделать их производными.
17

18.

Описание объектного типа строится по схеме:
вариант_типа имя_типа[: список_базовых_классов]
{ компоненты (члены) класса}
Имеются три варианта объектных типов:
структура (struct), объединение (union) и класс
(class), различающиеся возможностями доступа к
компонентам типа.
Компонентами класса могут быть компонентыданные и компоненты-функции.
Компоненты-функции
предназначены
для
выполнения операций над объектными данными,
их называют методами класса.

19.

Для каждого компонента класса устанавливается
уровень доступа – явно, указанием уровня
доступа одним из ключевых слов public, private
или protected с двоеточием, либо неявно, по
умолчанию.
Указание уровня доступа относится ко всем
последующим компонентам класса, пока не
встретится указание другого уровня доступа.
Уровень доступа public [‘паблик] (общие
Уровень
Уровень доступа
доступа
privateprotected
[‘прайвит]
(внутренние
(доступные)
компоненты
класса) – [прэ’тэктид]
разрешает
(недоступные)
(защищенные
компоненты
компоненты
класса)
класса)
имеет
разрешает
смысл
доступ
к компонентам
класса
из любого
места
доступ
только к вкомпонентам
класса
системе
только
из
классов
методов
программы,
виерархической
котором известна
переменная
этогои
этого
разрешает
класса
доступ ик компонентам
функциям, этого
объявленным
уровня из
класса.
дружественными
методов производного
данному
класса
классу.
(потомкам).

20.

По умолчанию к компонентам класса типа class
уровень доступа private, явно можно определять
и другие уровни, для всех компонент класса типа
struct по умолчанию принимается уровень
доступа public, но можно явно задавать и другие
уровни доступа, для класса типа union уровень
доступа public и не может быть изменен.
Члены-данные класса, определяющие его
состояние, как правило, помещаются в privateили protected-область класса – они не должны
быть непосредственно доступны извне класса.
Доступ к состоянию класса должен определяться
только интерфейсом класса.
Методы класса могут размещаться в любой
области видимости класса.

21.

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

22.

Будем рассматривать класс «рациональная дробь»
– Rational [‘рэшенел].
Состояние класса: два поля типа «целое», с
именами num (от numerator [‘нью:мерэйте] –
числитель) и den (от denominator [ди’номенэйте]
– знаменатель.
Пока ограничиваемся диапазоном представления
в стандартных типах.
Дополнительные требования: знаменатель не
должен быть равен нулю, ни при каких условиях;
знаменатель всегда положителен, знак дроби
определяется знаком числителя; поля класса не
должны
быть
доступны
извне
класса
непредусмотренными классом способами.

23.

Методы класса: традиционные арифметические
операции (сложение, умножение и т.п.),
ввод/вывод;
кроме
того,
потребуются
вспомогательные операции для выполнения
арифметических операций – типа сокращения
дроби и т.п.

24.

Определение класса
Синтаксис определения класса.
определение
методов
класса
(определение
прототипов функций, которые обеспечат необходимую
обработку информации). На этом этапе приводится
словесное описание того, что мы хотим получить от
класса, не указывая, как мы этого добьемся.
сlass имя_класса{
struct имя_класса{
уровень_видимости:
уровень_видимости:
описания_полей_класса
описания_полей_класса
прототипы_функцийпрототипы_функцийметодов_класса
методов_класса
уровень_видимости:
уровень_видимости:
. . .
. . .
};
};
Уровень_видимости задается одним из трех ключевых слов:
private; protected; public.

25.

Методы класса определены для класса, предназначены
Объявляем экземпляр нового типа данных X – в
для обработки информации, определяющей состояние
соответствии с обычными правилами (независимо от
класса, поэтому всегда видят его (состояние).
того, определен класс с помощью struct или class):
Описания_полей_класса
и
прототипы_функций
X obj;
определяются в соответствии с обычными правилами
С++
class X{ Тогда обращения:
struct X{
– вызовут сообщения
private: obj.a1, obj.a2, obj.f1()
private:
об ошибке (члены класса a1, a2 и f1() не
int a1;
int a1;
видны (не доступны) извне класса;
void f1();
void f1();
protected:obj.a3, obj.f3()–protected:
корректны.
char a2;
char класса
a2; f1() и f3()
Внутри функций-методов
public:
public:использовать все
можно без опасений
double имена:
a3; a1, a2, a3, f1()и
double
a3;
f3().
int f3();
int f3();
};
};

26.

Порядок следования ключевых слов, определяющих
уровень видимости, произволен; они могут
появляться неоднократно или отсутствовать в
определении класса. Если в начале определения
класса отсутствует уровень видимости, тогда для
class предполагается private, а для struct – public.
class X{
int a1;
void f1();
...
};
эквивалентно
class X{
private:
int a1;
void f1();
...
};
struct X{
int a1;
void f1();
...
};
эквивалентно
struct X{
public:
int a1;
void f1();
...
};

27.

Компоненты-данные класса всегда описываются
внутри класса.
Компоненты-функции могут быть описаны как
внутри, так и вне определения класса.
В последнем случае определение класса должно
содержать прототип функции, а заголовок функции
должен включать описатель видимости, который
состоит из имени класса и знака ::
<тип функции> <имя класса>::<имя функции> (<список параметров>)
{<тело компонентной функции> }
void X::f1( )
{ cout<<a1; }

28.

По правилам С++, если тело компонентной
функции размещено в описании класса, то эта
функция по умолчанию считается встраиваемой
(inline).
Коды встраиваемых функций компилятор
помещает непосредственно в место вызова, что
ускоряет работу программы.
Встраиваемую компонентную функцию можно
описать и вне определения класса, добавив к
заголовку функции описатель inline.
inline void X::f1( )
{ cout<<a1; }

29.

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

30.

Определение класса можно поместить перед
текстом программы или записать в отдельный
файл, который подключается к программе с
помощью директивы компилятора include:
#include “имя файла” – если файл находится в
текущем каталоге
#include <имя файла> – если файл находится в
каталогах автоматического поиска.
Объявление
класса
(или
структуры)
не
производит ни какого действия, в том числе и
выделения памяти.
Это описание нового типа переменной,
создаваемой программистом, поэтому при
объявлении запрещена инициализация.

31.

В программе, использующей определенные
ранее классы, по мере необходимости объявляют
объекты класса (экземпляры класса):
<имя класса> <список объектов или
указателей на объект>;
Например:
Х а, *b, c[5];
а
– объект класса Х;
*b
– указатель на объект класса Х;
c[5] – массив из 5 объектов класса Х;

32.

Обращение к полям и методам объявленного
объекта может осуществляться с помощью
полных имен, каждое из которых имеет вид:
<имя объекта>.<имя класса>::<имя поля
или функции>;
Например:
а.Х:: а1;
а.Х:: f1();
b->X:: f1();
c[i].X:: а1;
Однако чаще доступ к компонентам объекта
обеспечивается с помощью укороченного имени,
в котором имя класса и двойное двоеточие
опускают (по аналогии с обращением к полям
структур).

33.

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

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

34.

По отношению к классу методы делятся на
следующие две категории:
функция-член класса – функция, принадлежащая
самому классу и не существующая вне класса;
прототипы функций-членов класса включены в
определение класса;
функция-друг класса – внешняя по отношению к
классу функция, которая может существовать вне
класса, но имеет доступ к закрытой (и защищенной)
части класса. Прототип функции-друга класса также
включается в определение класса, но начинается
специальным ключевым словом friend.

35.

Конструкторы и деструктор класса могут быть
реализованы только функциями-членами класса
и имеют специальный синтаксис.
Другие методы класса имеют обычный
синтаксис функций языка С++ и могут быть
реализованы
и
функциями-членами,
и
функциями-друзьями класса. Пока ограничимся
рассмотрением только функций-членов класса.

36.

Часто конструкторы определяют как методы,
предназначенные для создания экземпляров
класса. Хотя это не совсем корректно, так как
экземпляр класса непосредственно создается (т.е.
под него выделяется память) не конструктором, а
соответствующими программными средствами, в
соответствии с определением языка. Так,
например, при определении локальных объектов
память под них выделяется в момент вызова
функции, в которой эти объекты определяются;
при использовании динамических объектов
память выделяется при выполнении оператора
new.

37.

Конструктор непосредственно память не
выделяет, но вызывается для инициализации
выделенной памяти, т.е. в момент создания
экземпляров класса. Однако при выполнении
каких-либо вычислений может потребоваться
создание временных экземпляров класса,
которые уничтожаются по окончании этих
вычислений; для создания таких временных
экземпляров
класса
также
используется
конструктор.

38.

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

39.

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

соответствии с требованиями задачи).

40.

Копирующий конструктор инициализирует
состояние класса значением другого экземпляра
этого класса (создает копию существующего
экземпляра класса). В списке параметров
указывается единственный параметр, имеющий
тип «ссылка на экземпляр класса».
По тому, кто определяет конструкторы,
последние делятся на конструкторы по
умолчанию (не требуют какого-либо упоминания
в определении класса) и явно определенные
программистом.

41.

Конструкторы по умолчанию: только пустой и
копирующий. Пустой конструктор по умолчанию не
инициализирует
состояние
экземпляра
класса.
Копирующий конструктор по умолчанию при
инициализации осуществляет побайтное копирование
состояния указанного существующего экземпляра
класса.
Конструкторы, определенные программистом: любые.
Они
осуществляют
инициализацию
состояния
экземпляра класса в соответствии с логикой,
определенной в конструкторе. Если программист
определяет свои конструкторы, будут использоваться
именно
они.
Если
есть
хотя
бы
один
инициализирующий
конструктор,
определенный
программистом, пустой конструктор по умолчанию не
используется (даже если программист не определил
собственный пустой конструктор).

42.

В определении конструкторов отсутствует тип
возвращаемого значения (конструктор ничего не
возвращает); имя конструктора совпадает с именем
класса; в классе может быть определено несколько
конструкторов.
Деструктор служит для разрушения экземпляра
класса. Опять же, память, занятая экземплярами
класса,
освобождается
в
соответствии
с
используемыми средствами языка; локальный объект
уничтожается, когда осуществляется выход за пределы
области видимости для этого объекта. Динамический
объект уничтожается при выполнении оператора delete.
Временный объект уничтожается по окончании
вычислений, в которых он используется. В момент
уничтожения объектов для них вызывается деструктор.

43.

В классе может быть определен только один
деструктор. Также существует деструктор по
умолчанию
или
явно
определенный
программистом.
Деструктор по умолчанию не выполняет
никаких действий.
Деструктор, определенный программистом,
выполняет
действия,
указанные
в
его
определении.
В определении деструктора также отсутствует
тип возвращаемого значения; имя деструктора
также совпадает с именем класса, но начинается
символом ~.

44.

Тип метода
Пустой
конструктор
Прототип
имя_класса();
Инициализиру
имя_класса(тип
ющие
параметр, ...);
конструкторы
Копирующий
конструктор
имя_класса(const
имя_класса & параметр);
Деструктор
~ имя_класса ();
Примечания
Инициализирует состояние
предопределенными
значениями
Тип – любой;
инициализирует состояние
значениями, заданными в
списке аргументов
Инициализирует состояние
значением указанного в
списке аргументов
экземпляра данного класса;

45.

Пример использования
Классов
Класс
Rational
Рациональная дробь

46.

#include <math.h>
#include <iostream >
using namespace std;
class Rational{
private: int num, den; // состояние класса - числитель и знаменатель дроби
int gcd(); // метод класса - нахождение наибольшего общего делителя
void reduce();
// метод класса - сокращение дроби
void correct(); // метод класса - коррекция дроби
public:
/* Конструкторы класса:
Rational(){num = 0; den = 1; } /* пустой; инициализирует дробь
значением 0 */
Rational(int num) {Rational::num = num; den = 1; }
/* Инициализирующий с 1 аргументом; инициализирует дробь целым значением */

47.

Rational(int n, int d) {num = n; den = d; correct();
}
/* Инициализирующий с 2 аргументами; инициализирует дробь
заданным значением */
~Rational(){} /* Деструктор класса */
/* Методы класса: селекторы */
void print();
Rational add(const Rational &opd);
void assign(int x, int y); /* Модификатор */
};

48.

inline void Rational::correct()
{
if (den = = 0)
den = 1;
if (den < 0)
num = -num, den = -den;
}
inline void Rational::assign(int x, int y)
{
num = x;
den = y;
correct();
}

49.

int Rational::gcd()
{
int n = abs(num), d = den, r;
while(r = n % d) // вычисляется остаток от деления и
n = d, d =
return d;
сравнивается с 0
r; // переопределяются делимое и делитель
}
void Rational::reduce() // Сокращение дроби
{
int div = gcd();
num /= div;
den /= div;
}

50.

// Сложение дробей
Rational Rational::add(const Rational &opd)
{
Rational temp;
temp.num = num * opd.den + den * opd.num;
temp.den = den * opd.den;
temp.reduce();
return temp;
}

51.

// Вывод значения дроби в выходной поток
void Rational::print()
{
cout << num;
if(den > 1)
cout << '/'<< den;
}

52.

{
int main()
Rational a(2), b[3], x, y(3,7);
const Rational c(5,8);
a.print(); cout << endl; // Вывод значения дроби a
b[1].print(); cout << endl; // Вывод значения элемента массива b
x = a.add(c); // Сложение значений дробей a и c
x.print(); cout << endl; // Вывод результата сложения
x.add(Rational(3,5)).print(); cout << endl;
// Сложение дроби x с дробью 3/5 и вывод результата
Rational *ptr;
ptr = new Rational(3,8);
(*ptr).print(); cout << endl; /* Возможна и запись ptr->print(); */
}

53.

Еще пример – решение основной задачи (система
двух уравнений с двумя неизвестными).
Предполагается, что для класса Rational
определены все арифметические операции:
сложения (add), вычитания (sub), умножения (mul)
и деления (div).
Решить систему вида: ax+by=c
dx+ey=f
Значения коэффициентов системы заданы, например:
a b c d e f
2 3 -1 5 2 3
Решение имеет вид:
определитель системы det = a * e - d * b;
x = (c * e - b * f) / det;
y = (a * f - d * c) / det;

54.

Чтобы умножить a на e, нужно экземпляру a
послать сообщение: «умножь себя (свое значение)
на e»: a.mul(e);
main()
{
Rational a(2), b(3), c(-1), d(5), e(2), f(3), x, y;
Rational det;
det = (a.mul(e)).sub(d.mul(b));
x = (c.mul(e)).sub(b.mul(f)).div(det);
y = (a.mul(f)).sub(d.mul(c)).div(det);
x.print(); cout << ','; y.print(); cout << endl;
}

55.

Использование модификатора const
Модификатор const может использоваться для
объявления функций-членов класса (селекторов),
которые не будут изменять состояние класса и
сообщают об этом компилятору. Это дает
возможность использовать такие функции для
экземпляров класса, объявленных как константы,
например:
class X{
private:
int n;
public:
X(int a = 0){n = a;}
int getval(){return n;}
X& setval(int a) {n = a; return *this;}
};

56.

Если в программе объявлены экземпляры класса:
X ob1(5);
const X ob2(5);
тогда:
ob1.setval(4);
ob2.setval(4);
ошибка
компиляции
(значение
константы не может быть изменено)
int k = ob1.getval();
int l = ob2.getval();
ошибка
(непредусмотренное использование константного объекта
ob2), т.к. компилятор ничего не знает о том, что метод
getval() не изменяет состояние адресата, а адресатом
является константа.

57.

Если изменить класс X следующим образом:
class X{
private:
int n;
int getval(){return n;}
public:
X(int a = 0){n = a;}
int getval() const {return n;}
X& setval(int a){n = a; return *this;}
};
class X{
private:
int n;
public:
X(int a = 0){n = a;}
int getval() const;
X& setval(int a){n = a; return *this;}

58.

Если изменить класс X следующим образом:
class X{
private:
int n;
int getval(){return n;}
public:
X(int a = 0){n = a;}
int getval() const {return n;}
X& setval(int a){n = a; return *this;}
};
Тогда все будет в порядке, и вызов
int l=ob2.getval(); вернет корректное значение.
Если реализация метода приводится вне определения
класса, тогда модификатор const должен появиться в двух
местах: в прототипе функции-члена класса и в определении
функции:

59.

Перегруженные функции – это функции с одним и
тем же именем, но имеющие разные списки
параметров. Параметры могут отличаться типами
и/или количеством. Тип возвращаемого функцией
значения во внимание не принимается.
Примеры:
void f(int); void f(char); void f(long);
void f(float, int); void f(int, int, int);
Функции с параметрами, заданными по умолчанию
- пример перегрузки функций.

60.

Правила перегрузки операторов: только унарные и
бинарные, почти все операторы; составное имя
функции –
operator знак_операции.
Приоритет оператора, правило ассоциативности и
количество операндов изменить нельзя!
Пример перегрузки бинарного оператора (сложение
для класса Rational):

61.

class Rational{
public: ...
Rational operator +(Rational r);
...
}
Rational Rational::operator +(Rational r)
{
Rational tmp;
tmp.num = num*r.den + den*r.num;
tmp.den = den*r.den;
tmp.reduce();
return tmp;
}

62.

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

63.

Правила сравнения:
1. Точные совпадения
2. Расширения
3. Стандартные преобразования
4. Преобразования,
требующие
временные
переменные
5. Преобразования, определенные пользователем
Преобразования,
требующие
временные
переменные: параметр объявлен как ссылка, а
аргумент требует преобразования (например,
преобразование из float в int&) или задан
выражением, значение которого не может быть
изменено.

64.

Примеры
void print(int);
void print(const char *);
void print(double);
void print(long);
void print(char);
char c; int i; short s; float f;
print(c); // правило 1; вызывается print(char)
print(i); // правило 1; вызывается print(int)
print(s); // правило 2; вызывается print(int)
print(f); // правило 2; вызывается print(double)
print( a );
// правило 1; вызывается print(char)
print(49);// правило 1; вызывается print(int)
print( a );
// правило 1; вызывается print(const
char *)

65.

Пример с ошибками
void f(int, float);
void f(float, int);
Вызов, который приведет к генерации
сообщения об ошибке (неоднозначный выбор):
f(1.5, 1.5);
English     Русский Rules