Похожие презентации:
Методы и стандарты программирования
1. Методы и стандарты программирования Семинары
МЕТОДЫ И СТАНДАРТЫ ПРОГРАММИРОВАНИЯСЕМИНАРЫ
II семестр
2. C и C++
2C И C++
Семинар 1
3. Отчетность
3Отчетность
Работа на семинарах + задачи
2. Две лабораторные работы (C++, Qt)
3. Экзамен
1.
4. Совместимость Си и C++
4Совместимость Си и C++
• C++ создавался как потомок до стандартизированного C, по большей
части совместимый с ним на тот момент на уровне исходного кода и
компоновки.
• Средства разработки для обоих языков (такие, как среды разработки и
компиляторы) часто интегрируются в один продукт.
• C не является подмножеством C++, поэтому нетривиальные программы
на C могут не компилироваться на C++ без изменений.
• C++ вводит множество возможностей, недоступных в C, и на практике
почти весь код, написанный на C++, не соответствует коду на C.
5. Стандарты C++
5Стандарты C++
6. Общие различия
6Общие различия
C
C++
Код читается проще
Код выглядит сложнее
Проще портировать код на другие платформы
Из-за особенностей языка код портировать сложнее
Есть структуры и переменные типа «структура»
Есть классы и объекты
Ориентирован на структурное и процедурное
программирование
ООП
Не поддерживаем инкапсуляцию, данные могут
редактироваться внешним кодом
Инкапсуляция скрывает данные, чтобы гарантировать, что
структуры данных и операторы используются по назначению
Исключения нужно конструировать самому
Есть обработка исключений
Не поддерживает перегрузку функций и операторов
Поддерживает перегрузку функций и операторов, а также
пространства имен и ссылки, обработка исключений
Почти всё стандартно и предсказуемо
Легко написать код, в котором непонятно, где ошибка
7. Типы данных
7Типы данных
Запрещается делать массивы переменной длины (Variable
Length Arrays, VLA).
Тип перечислений в C это –int, в C++ – это тип самого
перечисления.
8. Приведение типов
8Приведение типов
Язык C++ сильнее типизирован, нежели C, т.е. данные
конструкции скомпилируется на C, а вот на C++ нет.
void* ptr;
int* i_ptr = ptr;
int* imass = malloc(128 * sizeof(int));
В C++ необходимо использовать явное приведение типа.
int* imass = (int*)malloc(128 * sizeof(int));
но в таком варианте приведения компилятор выдаст предупреждение
9. Warning: use of old-style cast
9Warning: use of old-style cast
int* imass = (int*)malloc(128 * sizeof(int));
int* imass = static_cast<int*>(malloc(128 * sizeof(int)));
Для корректного приведения типов есть следующие операторы приведения:
• static_cast<T>(e) – преобразование выражения одного статического типа в значение
другого статического типа;
• const_cast<T>(e) – отбросить квалификатор const у изначально не константных
данных или добавить квалификатор const;
• reinterpret_cast<T>(e) – приведение типов без проверки, т.е. непосредственное
указание компилятору, используется только в случае полной уверенности
программиста в собственных действиях;
• dynamic_cast<T>(e) – преобразует тип данных (указатель или ссылку) вниз по
иерархии наследования (из типа-предка в тип-потомок), с проверкой с помощью
динамической идентификации типа данных.
10. Функции стандартной библиотеки С
10Функции стандартной библиотеки С
C++ изменяет некоторые функции стандартной библиотеки
языка Си, добавляя дополнительные перегруженные
функции с квалификатором типа const, например, strchr
возвращает char* в C, в то время как C++ поступает так, как
если бы существовали две перегруженные функции:
const char *strchr(const char *)
char *strchr(char *)
11. Объекты с квалификатором const
11Объекты с квалификатором const
typedef struct
{
const int i = 65;
} Dot;
C–❌
Инициализировать
можно только при
создании объекта!
C++ – ✅
12. Символьные литералы
12Символьные литералы
Символьные литералы, такие как 'a', имеют тип int в C и тип
char в C++, это означает, что sizeof 'a' обычно даёт разные
результаты на двух языках: в C++ это будет 1, в то время как
в C это будет sizeof(int).
Как ещё одно следствие этого различия в типах, в C 'a' всегда
будет выражением со знаком, независимо от того, является
char знаковым или беззнаковым, тогда как для C++ это
зависит от реализации компилятора.
13. Логический тип bool
13Логический тип bool
И C99, и C++ имеют логический тип bool с константами true
и false, но они определены по-разному. В C++ bool — это
встроенный тип и зарезервированное ключевое слово.
В C99 новое ключевое слово _Bool вводится как новый
логический тип. Заголовок stdbool.h содержит макросы bool,
true и false, которые определены как _Bool, 1 и 0,
соответственно. Следовательно, true и false имеют тип int в
C.
14. Программа на языке C++
14Программа на языке C++
// подключаем заголовочный файл iostream
#include <iostream>
int main() // определяем функцию main
{
// выводим строку на консоль
std::cout << "Hello World!" << std::endl;
return 0;
}
15. Изменения по сравнению с Си и работой в прошлом семестре
15Изменения по сравнению с Си и работой в прошлом семестре
• Компилятор: gcc g++
• Расширения файлов: c cpp, h hpp/h (при этом
расширение hpp используется довольно редко)
16. Ввод/вывод C++
16Ввод/вывод C++
Функции ввода/вывода языка C++ находятся в заголовочном
файле iostream.
• std::cout – поток стандартного вывода;
• std::cerr – поток стандартного вывода ошибок;
• std::cin – поток стандартного ввода.
Для работы с потоком вывода используется оператор <<, с
потоком ввода >>.
Потоки ввода/вывода работают с любыми
фундаментальными типами
17. Ввод/вывод C++
17Ввод/вывод C++
printf(…) stdout std::cout <<
scanf(…) stdin std::cin >>
stderr std::cerr
#include <iostream>
int main(int argc, char **argv)
{
int i = 0;
double d = 0.;
char ch = 0;
Вывод:
299 0.1234555 7
i = 299 d = 0.123455 ch = 7
error: 299
Вывод:
a 0.1 c
i = 0 d = 0 ch =
error: 0
std::cin >> i >> d >> ch;
std::cout << "i = " << i << " d = " << d << " ch = " << ch << "\n";
std::cerr << "error: " << i << "\n";
return 0;
}
18. Манипуляторы ввода/вывода
18Манипуляторы ввода/вывода
• std::endl – перевод строки ~ \n;
• std::setprecision(int) – установка точности вывода вещественных
типов (количество знаков);
• std::scienti c – “научный” формат вывода вещественных чисел
(1.000000e-02);
• std::fixed – вывод вещественного числа с фиксированной точкой;
• std::hex – ввод/вывод данных в шестнадцатеричной системе;
• std::oct – ввод/вывод данных в восьмеричной форме;
• std::setw(int w) – установка ширины поля равной w;
• остальные можно посмотреть в документации (<iomanip>).
19. Пример использования манипуляторов IO
19Пример использования манипуляторов IO
#include <iostream>
#include <iomanip>
int main(int argc, char **argv)
{
int code = 120;
std::cout << code << " " << std::hex << code << " " << std::oct << code << std::endl;
double _pi = 3.14159265359;
std::cout << std::setprecision(4) << _pi << " " << std::scientific << _pi << std::endl;
std::cout << std::fixed << std::setw(6) << std::setprecision(2) << _pi << std::endl;
return 0;
}
Вывод:
120 78 170
3.142 3.1416e+00
3.14
20. Динамическое выделение памяти
20Динамическое выделение памяти
#include <iostream>
• Выделение памяти:
new, new[]
• Очистка памяти:
delete, delete[]
Вывод:
3
[0]0 [1]1 [2]4 [3]9 [4]16 [5]25
[6]36 [7]49 [8]64 [9]81
int main(int argc, char** argv)
{
int* i = new int;
*i = 3;
std::cout << *i << std::endl;
delete i;
const size_t arr_size = 10;
int* mass = new int[arr_size];
for (size_t i = 0; i < arr_size; ++i)
{
mass[i] = i * i;
std::cout << "[" << i << "]" << mass[i] << " ";
}
delete[] mass;
return 0;
}
21. Пространства имен
21Пространства имен
– это область определения переменных, типов, констант и
функций, объединенная в один или несколько блоков.
Позволяет избежать конфликта имен, а также повторно
использовать одни и те же именования переменных, функций
и т.д.
namespace Name
{
// Объявление переменных, констант, типов, функций
}
22. Пространства имен
22Пространства имен
Пространство имен может быть безымянным, что обычно
используется для сокрытия каких-либо определений внутри одного
модуля.
Пространства имен могут быть вложенными и повторяться в
разных модулях (файлах).
// доступ к элементу ПИ
namespace MATH {
double PI = 3.14;
}
std::cout << MATH::PI << std::endl;
23. using
23using
Доступ к элементам может быть упрощен с помощью
использования ключевого слова using, примененного либо к
пространству имен целиком, либо же к отдельному его
элементу:
// Позволит не указывать везде в коде std::
using namespace std;
// Позволит не указывать std:: для функции cin
using std::cin;
24. Доступ к элементам
24Доступ к элементам
Элементы, объявленные вне пространства имен,
принадлежат к глобальному пространству имен, и при
совпадении имен доступ к ним осуществляется следующим
образом:
void sqrt() {}
имен
namespace MATH {
void sqrt() {}
}
::sqrt();
MATH::sqrt();
sqrt();
// Функция, объявленная в глобальном пространстве
// Функция, объявленная в пространстве имен MATH
// Вызов функции из глобального пространства имен
// Вызов функции из пространства имен MATH
// Ошибка компиляции - неоднозначный вызов функции
25. Ссылки
25Ссылки
В C роль ссылок играют указатели, но работать с ними
бывает не очень удобно, поэтому в C++ появилась отдельная
сущность — ссылка (reference).
Ссылка – если T некоторый тип и есть переменная типа T, то
переменная типа T со спецификатором & будет ссылкой на
эту переменную, если она инициализирована этой
переменной.
T x;
T &rx = x; // rx это ссылка на x
26. Ссылки
26Ссылки
• не могут быть NULL;
• не требуют разыменования;
• не может быть ссылки на ссылку;
• нельзя итерироваться (смещаться), так как это не область
памяти.
27. Пример
27Пример
#include <iostream>
void swap(int &a, int &b)
{
int t = a;
a = b;
b = t;
}
void swap(int *a, int *b)
{
int tmp = *a;
*a = *b;
*b = tmp;
}
int main(int argc, char **argv)
{
int a = 10, b = -10;
std::cout << "Before: " << a << " " << b << std::endl;
swap(&a, &b);
std::cout << "After: " << a << " " << b << std::endl;
std::cout << "Before: " << a << " " << b << std::endl;
swap(a, b);
std::cout << "After: " << a << " " << b << std::endl;
return 0;
}
Вывод:
Before: 10 -10
After: -10 10
Before: -10 10
After: 10 -10
28. Задачи
28Задачи
Написать функцию, меняющую значения двух
переданных переменных местами без использования
указателей
2. Написать функцию, возвращающую указатель на
динамический массив значений факториала N. Значение
N запрашивается у пользователя и вводится с клавиатуры.
Вывести полученный массив на экран. Очистить память.
1.
29. Введение в ООП
29ВВЕДЕНИЕ В ООП
Семинар 2
30. Принципы ООП
30Принципы ООП
• наследование – это процесс, посредством которого один
объект может приобретать свойства другого,
• инкапсуляция – это механизм, который объединяет данные
и код, манипулирующий этими данными, а также защищает
и то, и другое от внешнего вмешательства или
неправильного использования,
• полиморфизм – один интерфейс, много методов. Как
пример: перегрузка функций.
31. Классы и объекты
31Классы и объекты
Класс – логическая абстракция, которая реально не
существует до тех пор, пока не будет создан объект этого
класса, т.е. то, что станет физическим представлением этого
класса в памяти компьютера.
Cat cat; // создание объекта cat класса Cat
32. Конструкторы и деструкторы
32Конструкторы и деструкторы
Конструктор вызывается при создании объекта класса. Как правило,
конструктор используется для:
• выделения памяти под объект класса;
• начальной инициализации внутренних данных класса.
Деструктор – специальный метод, который вызывается при удалении
объекта.
Деструктор – это обратная по отношению к конструктору функция.
33. Виды конструкторов
33Виды конструкторов
• по умолчанию
• параметризованный
• копирования
34. Конструктор копирования
34Конструктор копирования
В C++ полная копия объекта нужна в следующих случая:
1. В момент объявления нового объекта и его инициализации
данными другого объекта с помощью оператора =.
2. Когда нужно передать объект в функцию как параметрзначение.
3. Когда нужно вернуть объект из функции по значению. ! Если в
классе не объявлен конструктор копирования, то используется
конструктор копирования, который автоматически
генерируется компилятором. Этот конструктор копирования
реализует побитовое копирование для получения копии
объекта.
35. Деструктор
35Деструктор
Имя деструктора совпадает с именем класса, перед которым
следует символ ‘~’.
Деструктор не может иметь параметров.
При объявлении класса, можно объявить только один
деструктор.
36. Rule of tree / rule of five
36Rule of tree / rule of five
Один из основополагающих принципов C++ – RAII
(Resource Acquisition Is Initialization — “получение ресурса
есть инициализация”).
37. Rule of tree / rule of five
37Rule of tree / rule of five
Правило трёх (rule of three, закон
«Большой тройки»). Если классу
необходим один из следующих
методов:
деструктор,
конструктор копирования,
оператор копирующего
присваивания,
то класс должен определять все три.
С выходом С++11 правило трёх
превратилось в правило пяти (rule of
five): если классу необходим один из
следующих методов:
• деструктор,
• конструктор копирования,
• оператор копирующего
присваивания,
• конструктор перемещения,
• оператор перемещающего
присваивания,
то класс должен определять все пять.
38. Инкапсуляция
38Инкапсуляция
Для типа struct все данные и методы по умолчанию public.
Для типа class все данные и методы по умолчанию private.
В C++ доступно несколько спецификаторов, и они изменяют
доступ к данным следующим образом:
• публичные (public) – данные доступны всем;
• защищенные (protected) – доступны только классу и дочерним
классам;
• приватные (private) – доступны только классу которому они
принадлежат.
39. Инкапсуляция
39Инкапсуляция
40. Полиморфизм
40Полиморфизм
В языке C++ есть возможность реализовывать два вида
полиморфизма:
1. Статический. Этот вид полиморфизма достигается путём
использования перегруженных функций (раннее
связывание).
2. Динамический. В этом случае используется наследование
в сочетании с виртуальными функциями (позднее
связывание).
41. Статический полиморфизм
41Статический полиморфизм
«Перегрузка» функции – это объявление функции с тем же именем несколько раз. Чтобы
компилятор мог отличать «перегруженные» функции между собой, эти функции должны
отличаться списком входных параметров.
В общем случае, объявление перегруженной функции в некоторой области видимости
выглядит следующим образом:
return_type1 FunName(parameters_list_1)
{
// ...
}
return_type2 FunName(parameters_list_2)
{
// ...
}
return_typeN FunName(parameters_list_N)
{
// ...
}
42. Статический полиморфизм
42Статический полиморфизм
43. Пример перегрузки конструкторов в классе
43Пример перегрузки конструкторов в классе
44. Списки инициализации членов класса
44Списки инициализации членов класса
В данном примере в
конструкторе
используется
присваивание, а не
инициализация.
45. Списки инициализации членов класса
45Списки инициализации членов класса
Если в классе Point2d появится
константное поле, то его
необходимо будет
проинициализировать либо при
создании объекта, либо через
список инициализации.
Поскольку разрешено
инициализировать константные
переменные (но не присваивать
им значения!).
46. Списки инициализации членов класса
46Списки инициализации членов класса
Списки инициализации не
только позволяют
инициализировать
константы, но и работают
эффективнее (не
выполняется копирующая
инициализация).
47. Списки инициализации членов класса
47Списки инициализации членов класса
Если член класса –
массив, то список
инициализации
приобретает
следующий вид.
48. Задачи
48Задачи
1. Реализовать класс Human:
Конструкторы: Human(string name); Human(string name, int age);
Функции: printAge(), printName();
2. Реализовать класс Children
Определить класс Children с правильной декомпозицией по файлам.
Класс содержит такие закрытые поля: имя, фамилия, возраст.
Реализовать функции ввода данных и вывода их на экран. (для каждого поля своя функция)
Объявить 2 объекта класса, внести данные и показать их.
Все поля классов Human и Children должны быть реализованы с использованием
динамического выделения памяти(указателей). Необходимо обеспечить корректное
освобождение ресурсов при завершении работы программы.(деструкторы)
49. Задача*
49Задача*
Реализовать в собственном пространстве имен класс RGBA (конструктор по умолчанию (черный цвет), с
параметрами), хранящий в себе настройки цвета. Реализовать функции:
• setColor(r, g, b, a = 255) – принимает все значения цвета и устанавливает их (alpha по
умолчанию 255)
• setColor() – не принимает параметров, устанавливает всем цветам значение 0
• red(), green(), blue(), alpha() – возвращают значения конкретных цветов (константность)
• print(enum PrintType = E_RGBA) – печать на экран значений в соответствии с переданным
типом
• E_RGBA: в строку (Пример: 255 255 255 255)
• E_HEX: в hex-формате (Пример: `#ffffffff`)
⁎ E_INT: в числовом формате (int, побитовый сдвиг)
• sum(const &RGBA) – сложение цвета с переданным цветом, возвращает новый объект RGBA.
Декомпозировать реализацию на файлы, в функции main создать объект класса, протестировать все
реализованные функции.
50. Классы
КЛАССЫСеминар 3
51. Вспоминаем
51Вспоминаем
• Ссылки: для чего, отличия от указателей
• Чем классы отличаются от структур?
• Классы: инициализация и освобождение. Какая концепция C++?
• Виды конструкторов, их применение
• Правило при написании конструкторов/деструкторов
• Что будет если не написать конструкторы или деструктор?
• В каком пространстве имен находятся методы класса?
• Пример статического полиморфизма
• Для чего нужны списки инициализации?
52. Конструктор копирования
5253. Оператор копирующего присваивания
5354. Главная разница
54Главная разница
55. Тонкости С++
55Тонкости С++
56. Задача 1
56Задача 1
1. Реализовать класс Matrix для хранения прямоугольной матрицы вещественных чисел типа double в динамической
памяти. Реализовать:
Приватные поля:
• rows – количество строк.
• Cols – количество столбцов.
• Data – указатель на динамический массив элементов матрицы.
Функции:
• Matrix(int rows, int cols, double value) – конструктор, создает матрицу заданного размера и заполняет
значениями value.
• ~Matrix() – деструктор класса.
• set(int row, int col, double value) – устанавливает на заданную позицию значение value.
• get(int row, int col) – возвращает значение элемента матрицы на заданной позиции.
• getRows() – возвращает количество строк в матрице.
• getCols() – возвращает количество столбцов.
• print() – выводит матрицу на экран в виде таблицы.
Реализовать для класса Matrix конструкторы копирования и копирующего присваивания.
Декомпозировать реализацию на файлы(Matrix.h, Matrix.cpp, main.cpp) в функции main создать объекты класса и
протестировать все реализованные функции.
57. Дополнительная задача
57Дополнительная задача
Реализовать в собственном пространстве имен класс односвязного списка ForwardList,
хранящий значения типа double. Реализовать:
• внутреннюю структуру узла Node
• push_front(double) – добавление элемента в начало списка
• pop_front() – удаляет первый элемент
• front() – возврат ссылки на первый элемент (перегрузка)
• size() – возврат количества элементов в списке
• empty() – проверяет, пуст ли список
• display() – вывод списка на экран
• erase(double) – удаляет элементы с переданным значением
• clear() – очистка и освобождение памяти
Декомпозировать реализацию на файлы, в функции main создать объект класса,
протестировать все реализованные функции.
58. Перегрузка операторов
ПЕРЕГРУЗКА ОПЕРАТОРОВСеминар 4
59. Правило трех
59Правило трех
В чем оно заключается?
class ForwardList
{
Node *head;
public:
ForwardList(); // default constuctor
ForwardList(const ForwardList&); // copy constructor
~ForwardList(); // destructor
// ...
};
60. Оператор копирующего присваивания
60Оператор копирующего присваивания
class ForwardList
{
public:
ForwardList();
ForwardList(const ForwardList&);
??? operator=(const ForwardList& list) {
Node *current = list.head;
while (current) {
// ...
}
}
~ForwardList();
};
61. Оператор копирующего присваивания
61Оператор копирующего присваивания
class ForwardList
{
public:
ForwardList();
ForwardList(const ForwardList&);
ForwardList& operator=(const ForwardList& list) {
Node *current = list.head;
while (current) {
// ...
}
return *this;
}
~ForwardList();
};
62. Safe for self-assignment
62Safe for self-assignment
class ForwardList
{
public:
ForwardList();
ForwardList(const ForwardList&);
ForwardList& operator=(const ForwardList& list) {
if (this == &list)
return *this;
// ...
return *this;
}
~ForwardList();
// ...
};
C.62: Make copy assignment safe for self-assignment
63. Перегрузка операторов (operator overloading)
63Перегрузка операторов (operator overloading)
• возможность применять встроенные операторы языка к
разным типам, в том числе и пользовательским;
• повышает читаемость кода, делает его более лаконичным и
выразительным, упрощает написание обобщенного кода;
• в C++ перегрузка операторов имеет серьезную поддержку и
активно используется в стандартной библиотеке.
64. Перегружаемые операторы
64Перегружаемые операторы
Если не рассматривать операторы
управления памятью (new, new[],
delete, delete[]), то условно
остальные можно разделить на
следующие типы:
• унарные (один параметр)
• бинарные (два параметра)
• операторы +, -, *, &, ++, -- (оба
варианта)
• оператор () (произвольное
число параметров)
65. Перегружаемые операторы
65Перегружаемые операторы
• Необходимо учитывать приоритет и ассоциативность операторов
• Не рекомендуется перегружать следующие три бинарных
оператора: , (запятая), &&, ||. Стандарт для них
предусматривает порядок вычислений, а для последних двух –
семантику быстрых вычислений (short-circuit evaluation), что не
гарантируется для перегруженных операторов.
• Не рекомендуется перегружать унарный оператор & (взятие
адреса). Тип с перегруженным оператором & опасно использовать
с шаблонами, так как они могут использовать стандартную
семантику этого оператора (std::addressof() с C++11 может
помочь).
66. Реализация перегрузки операторов
66Реализация перегрузки операторов
Операторы можно перегружать в двух вариантах: как
функцию-член класса и как свободную функцию.
Четыре оператора можно перегрузить только как функциючлен класса: =, ->, [], ().
Для перечислений, операторы можно перегружать только как
свободные функции.
Операторы << и >> нельзя перегрузить через функцию-член
класса.
67. Перегрузка оператора как функция-член класса
67Перегрузка оператора как функция-член класса
• объявить нестатическую
функцию-член
return_type operator#()
• в случае перегрузки унарного
оператора эта функция не
должна иметь параметров,
• в случае бинарного должна
иметь ровно один параметр,
• в случае перегрузки оператора
() эта функция может иметь
произвольное число
параметров.
class Complex
{
public:
Complex(int r = 0, int i = 0) {
real = r;
img = i;
}
// return_type operator#(operands)
Complex operator+(const Complex &obj) {
Complex res;
res.real = real + obj.real;
res.img = img + obj.img;
return res;
}
void print() {
std::cout << real << " " << img << std::endl;
}
private:
int real, img;
};
68. Перегрузка оператора как функция-член класса
68Перегрузка оператора как функция-член класса
int main(int argc, char **argv)
{
Complex comp1(4, -2), comp2(3, 5);
Complex comp3 = comp1 + comp2;
comp3.print();
return 0;
}
Вызов можно представить следующим
образом:
comp1.operator+(comp2)
class Complex
{
public:
Complex(int r = 0, int i = 0) {
real = r;
img = i;
}
// return_type operator#(operands)
Complex operator+(const Complex &obj) {
Complex res;
res.real = real + obj.real;
res.img = img + obj.img;
return res;
}
void print() {
std::cout << real << " " << img << std::endl;
}
private:
int real, img;
};
69. Выражение операторов в терминах других
69Выражение операторов в терминах других
class Complex
{
public:
Complex(int r = 0, int i = 0) : real(r), img(i) {}
Complex& operator+=(const Complex &obj) {
real += obj.real;
img += obj.img;
return *this;
}
Complex operator+(const Complex &obj) const {
return Complex(*this) += obj;
}
private:
int real, img;
};
70. Приведение типов и конструкторы
70Приведение типов и конструкторы
struct Rational {
int num;
int denum;
Rational(int n = 0, int d = 1) : num(n), denum(d) {}
};
int main()
{
Rational r1(1, 2);
Rational r2 = 2;
return 0;
}
71. Приведение типов и конструкторы
71Приведение типов и конструкторы
struct Rational {
int num;
int denum;
Rational(int n = 0, int d = 1) : num(n), denum(d) {}
};
int main()
{
Rational r1(1, 2); // OK
Rational r2 = 2;
return 0;
}
72. Приведение типов и конструкторы
72Приведение типов и конструкторы
struct Rational {
int num;
int denum;
Rational(int n = 0, int d = 1) : num(n), denum(d) {}
};
int main()
{
Rational r1(1, 2); // OK
Rational r2 = 2; // OK
return 0;
}
73. Ключевое слово explicit
73Ключевое слово explicit
struct Rational {
int num;
int denum;
explicit Rational(int n = 0, int d = 1) : num(n), denum(d) {}
};
int main()
{
Rational r1(1, 2); // OK
Rational r2 = 2; // ERROR (no suitable constructor exists to convert from "int" to "Rational")
return 0;
}
74. Ключевое слово explicit
74Ключевое слово explicit
• Ключевое слово explicit используется для предотвращения
неявных преобразований типов
• Применяется к конструкторам, которые принимают один
аргумент (или несколько аргументов, но только один из них
может быть неявно преобразован)
• Избегает неожиданных преобразований типов, которые
могут привести к ошибкам
75. Перегрузка оператора как функция-член класса
75Перегрузка оператора как функция-член класса
76. Перегрузка оператора как функция-член класса
76Перегрузка оператора как функция-член класса
Если преобразование типов
должно быть применимо ко всем
параметрам функции (включая и
скрытый параметр this), то
функция не должна быть членом
класса.
77. Операторы << и >>
77Операторы << и >>
std::ostream& operator<<(std::ostream &out, const Complex &obj);
• При перегрузке через метод класса в качестве левого операнда
используется текущий объект. В этом случае левым операндом является
объект типа std::ostream.
• Мы не можем использовать std::ostream в качестве левого неявного
параметра, на который бы указывал скрытый указатель *this, так как
указатель *this может указывать только на текущий объект текущего
класса, члены которого мы можем изменить, поэтому перегрузка
оператора << может осуществляться через дружественную функцию.
78. Перегрузка оператора как свободная функция
78Перегрузка оператора как свободная функция
• в случае перегрузки унарного оператора,
эта функция должна иметь один
параметр,
• в случае бинарного должна иметь два
параметра,
• в случае перегрузки бинарного оператора
– по крайней мере один из двух
параметров, а в случае унарного
единственный параметр, должен быть
того же типа (или типа ссылки), что и
тип, для которого реализуется перегрузка,
• функция должна находится в том же
пространстве имен, что и тип, для
которого реализуется перегрузка.
79. Пример
79Пример
80. Правила перегрузки операторов
80Правила перегрузки операторов
• тщательно продумывайте перегрузку операторов. Она должна повысить
наглядность и читаемость кода, но не наоборот;
• для унарных операторов используйте перегрузку через методы-класса;
• для бинарных операторов, которые изменяют левый операнд
(operator+= и т.д.), используйте перегрузку через методы класса,
если это возможно;
• для бинарных операторов, которые не изменяют левый операнд
(operator +() и т.д.), используйте перегрузку через
обычные/дружественные функции;
• eсли мы хотим реализовать обычные бинарные арифметические
операторы, удобнее сначала будет реализовать: += *= -= /= %=
81. Подробнее
81Подробнее
Программирование