1.21M
Категория: ПрограммированиеПрограммирование

Пользовательские типы данных в С++. Тема 2-6

1.

Тема 2-6.
Пользовательские типы данных
в С++
для АСУб и ЭВМб

2.

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

3.

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

4.

Абстрагирование в программировании
Алгоритмы
Структуры данных
Модули
Классы
Фреймворки, каркасы
Функции

5.

Композиция объектов
• Композиция объектов – комбинирование
объектов и свойств (подобъектов) для
формирования нового объекта
• Три фундаментальных формы композиции:
– Агрегация
– Конкатенация
– Делегирование
• Эти формы не взаимоисключающие
• Программирование – процесс композиции
и декомпозиции

6.

Агрегация
• Агрегация – процесс формирования объекта
из перечислимой коллекции подобъектов
• Агрегат – это объект, который содержит
другие объекты
• Каждый подобъект сохраняют свою
идентичность и может быть обратно
декомпозирован без потерь
• Существует большое количество конкретных
способов агрегации.
Пример – символы в строку:
`a`, `b`, `c`, `d` -> “abcd”

7.

Конкатенация
• Конкатенация – объект формируется путём
добавления новых свойств к существующему
объекту
• Конкатенация – объект формируется путём
добавления свойств других объектов
• Каждый подобъект может потерять свою
идентичность относительно своего происхождения
• В случае именованных свойств может возникнуть
коллизия с одинаковыми именами.
Пример – строки в строку:
“ab”, “cd” -> “abcd”

8.

Обсуждение по массивам
• 1, 2, 3, 4, 5
• [1, 2, 3, 4, 5 ] – агрегация массива из
чисел
• Дано: [1, 2] и [3, 4] [ [1, 2] , [3, 4] ]
– Вместо [i,j] правильно [i][j]

9.

Фабричные функции
• Фабричная функция – любая функция, которая
возвращает новый объект
– На самом деле не любая, так как, например, в ООП есть
специальная функция для создания объектов – конструктор
и смешивать её с фабричными функциями не надо в целях
избежания терминологической путаницы.
ConcatIntToArray: 1,2,3,4 => [1,2,3,4]
AggregateIntToArray: 1,2,3,4 => [1,2,3,4]
ConcatArrayFromArrays: [1,2,3],[4,5] =>[1,2,3,4,5]
AggregateArrayFromArrays:
• [1,2,3],[4,5] =>[ [1,2,3], [4,5] ] – это двумерный
массив, массив массивов

10.

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

11.

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

12.

Структуры С++: объявление
Объявление структурного типа выполняется
в виде шаблона, общий формат которого:
struct Имя_Типа {
Описание полей
};
Точка с
запятой
Структурный тип обычно декларируется в
глобальной области, т.е. до первой выполняемой
функции. Тогда его можно использовать во всех
функциях, входящих в проект.

13.

Структуры С++: обращения к подобъектам
Обращение к полям структур выполняется с помощью
составных имен, которые образуются двумя способами:
1) при помощи операции принадлежности ( . ) от
значения (имени структурной переменной) к полю:
Имя_Структуры . Имя_Поля
или
( *Указатель_Структуры ) . Имя_Поля
2) при помощи операции косвенной адресации ( –> ) от
адреса к полю
Указатель_Структуры –> Имя_Поля
или
( &Имя_Структуры ) –> Имя_Поля

14.

Структуры С++: переменные типа структура
После введения типа структура можно
переменные или массивы этого типа
struct Date
{
unsigned int Year;
char Month[12];
unsigned int Day;
char DayWeek[10];
}
int main()
{
Date D1 = new Date();
Date *ArDate = new Date[10];
}
задать

15.

Допускаются и другие варианты описания структурных
переменных. Можно вообще не задавать имя типа, а
описывать сразу переменные:
struct {char fam[30];
int kurs;
char grup[3];
float stip;
} studi, stud2, *pst;
В этом примере кроме двух переменных структурного
типа объявлен указатель pst на такую структуру. В
данном описании можно было задать имя структурного
типа student.

16.

Структуры С++: представление в памяти
#include<iostream>
using namespace std;
struct pixel {
unsigned char red;
unsigned char green;
unsigned char blue;
};
int main(int argc, char
*argv[])
{
int i,j;
// instantiations
pixel pixela;
pixel image[256][256];
...
return 0;
}

17.

Структуры С++: представление в памяти
#include<iostream>
using namespace std;
struct student {
char name[80];
int id;
int major;
};
int main(int argc, char
*argv[])
{
int i,j;
// instantiations
student s1;
...
return 0;
}

18.

Структуры С++: инициализация
Элементы структуры в памяти запоминаются последовательно, в том порядке, в
котором они объявляются: первому элементу соответствует меньший адрес
памяти, последнему - больший

19.

Структуры С++: присваивание
При присваивании
происходит полное
копирование

int main(int argc, char
*argv[])
{
student s1,s2;
strncpy(s1.name,”Bill”,80);
s1.id = 5; s1.major = CECS;
s2 = s1;
return 0;
}

20.

Структуры С++: вложенность
Элементом структуры может быть другая структура.
struct myfile {
char name[10];
char ftype[4];
int ver;
};
struct dir {
struct myfile f;
int
size;
} my_f [100];
Шаблон для вложенной структуры должен располагаться перед
определением фактической структурной переменной в рамках другой
структуры.
my_f [0].size
my_f [2].f.ver
//элемент size 1-ой структуры
//элемент ver вложенной структуры f в 3-й структуре my_f

21.

Структуры С++: использование в функциях
Для передачи информации о структуре внутрь функции
используются следующие способы:
Использование в качестве фактического аргумента элемента
структуры.
Использование в качестве фактического аргумента адреса структуры.
Использование в качестве фактического аргумента самой структуры.
Функция может возвращать только структуру, а не массив
Функция возвращает копию структуры

22.

Структуры С++: битовые поля
Битовые поля применяются для экономного хранения данных малого
диапазона, а также для работы с данными, в которых отдельные биты
имеют самостоятельное значение.
Битовое поле может быть объявлено только как элемент структуры.
Цепочка битов не должна превышать машинного слова.
/*контрольный байт*/
struct bit_area
{
unsigned char
er : 1; //бит ошибки
unsigned char
rd : 1; //бит готовности
unsigned char
dat : 6; //поле данных
}cntrl_byte;
Двоеточие

23.

24.

Пример со структурой в С++
В классе n учеников. Введите фамилии и оценки по 5 предметам
для каждого ученика, найдите средний балл и выведите на экран
фамилию и средний балл лучшего ученика.
#include <iostream>
#include <windows.h>
using namespace std;
int main()
{
SetConsoleCP(1251);
// установка кодовой страницы win-cp
1251 в поток ввода
SetConsoleOutputCP(1251); // установка кодовой страницы wincp 1251 в поток вывода
struct Student
{
string fam;
int p1, p2, p3, p4, p5;
float sr;
};
int n, i;

25.

Пример со структурой в С++: экран
продолжение
программы:
cin>>n;
Student a[n];
float m_b=0;
for(i = 0;i < n; i++)
{
cout << "Введите фамилию" << endl;
cin >> a[i].fam;
cout << "Введите 5 оценок" << endl;
cin >> a[i].p1 >> a[i].p2 >> a[i].p3 >> a[i].p4
>> a[i].p5;
a[i].sr = (a[i].p1+ a[i].p2 + a[i].p3 + a[i].p4 +
a[i].p5) / 5.;
}
for(i = 0; i < n; i++)
if(a[i].sr >= m_b) m_b = a[i].sr;
for(i = 0; i < n; i++)
if (a[i].sr == m_b)
cout << a[i].fam << " средний балл= « <<
a[i].sr << endl;
return 0;
}

26.

Пример со структурой в С++: запись в файл
о – значит out, т.е.
выходной поток
struct base
{
string name;
string work;
int year;
};
base a;
Используем также как
другие потоки, например,
стандартный поток
вывода cout
ofstream f;
f.open(“out.txt”);
Открыли и
закрыли
файловый
поток
f<<a.name<<“ “<<a.work<<“ “<<a.year<<endl;
f.close();

27.

Пример со структурой в С++: запись в файл
#include <fstream>
#include <string>
#include <windows.h>
using namespace std;
struct base
{
string name;
string work;
int year;
};
int main()
{SetConsoleCP(1251);
SetConsoleOutputCP(1251);
int n; //кол-во экземпляров
структуры//
cin>>n;
base b[n];
продолжение
программы
for (int i=0;i<n;++i)
{
cout << "Enter name :" << endl;
cin >> b[i].name;
cout << "Enter work :" << endl;
cin >> b[i].work;
cout << "Enter year :" << endl;
cin >> b[i].year;
}
ofstream outfile;
outfile.open(“out.txt");
for (int i = 0; i < n; ++i)
outfile << b[i].name << " " << b[i].work << " " <<
b[i].year << endl;
outfile.close();
return 0;
}

28.

Пример со структурой в С++: чтение из файла
i – значит in,
т.е. входной
поток
struct base
{
string name;
string work;
int year;
};
base a;
ifstream f;
f.open(“out.txt”);
Все операции
аналогичны
стандартным
потокам
f>>a.name>>a.work>>a.year;
f.close()

29.

Перечисляемый тип С++
Используется для объявления набора поименованных целых
констант.
Формат:
enum {<Ид>[=<Целое>] [,<Ид>[<>]…]}
<Список переменных>;
Пример:
Имя
переменной
enum {SUN, MON, TUES, FRI=5, SAT} day;
SUN =0, MON = 1, TUES = 2, FRI=5, SAT=6
Константы присваиваются, начиная с нуля или с указанного значения.

30.

Псевдонимы
typedef <Описание типа> <Имя объявляемого
типа>;
Примеры:
Имя
нового типа
1) typedef unsigned int word;
2) typedef enum {False, True} boolean;

31.

32.

Указатель на функцию
Любая функция имеет физическое местоположение в памяти.
Адрес функции является входной точкой в тело функции.
Этот адрес может быть присвоен
Следовательно, указатель на функцию может быть использован
для вызова функции.
В общем виде указатель на функцию определяется следующим
образом:
тип функции (*имя указателя функции)(список параметров);
Например, int (*fprt)(int);
Определяется указатель fprt, на функцию с целочисленным
параметром, возвращающую целочисленное значение.
указателю.

33.

Указатель на функцию: пример
Определим функцию f1, вычисляющую сумму двух чисел, и функцию f2,
вычисляющую разность этих же чисел.
double SUM (double a, double b)
{
return a + b;
}
double RAZ (double a, double b)
{
return a - b;
}
void main ( )
{
double q =-44, w = 70;
double d;
double (*ptr_f) (double, double);
/* объявили указатель на функцию*/
}
ptr_f = SUM;
d = (*ptr_f) (q, w);
/* ptr_f присвоили адрес SUM */
/* вызвали SUM по адресу */
ptr_f = RAZ;
d = (*ptr_f) (q, w);
/* присвоили адрес RAZ */
/* вызвали RAZ */

34.

Указатель на функцию:
параметром для некоторой функции
.
.
.
double ff(double a ,double b, double (*f)(double,double))
{
return f(a,b);
}
int main()
{
double (*f_p)(double, double);
.
.
.
cout<<endl<<endl<<"указатель на функцию-параметр"<<endl;
cout<<ff(3,4,SUM)<<endl;
cout<<ff(3,4,RAZ)<<endl;

35.

Делегирование
• Делегирование – объект перенаправляет
(или делегирует) к другому объекту, который
действует от его имени.
• Делегирование – механизм совместного
использования кода и данных между
объектами
• Делегирование – создание элемента одного
объекта в контексте другого.

36.

Указатель на функцию, псевдоноимы и
сквозной пример с обработкой
корректностью ввода
• Для задания имени указателя для
наглядности используйте using
– using validateFcn = bool(*)(int, int);
• Далее рассмотрите пример в файле
Сквозной пример III (на сайте курса)

37.

38.

Композиция данных и функций.
• Зачем вообще может понадобится? Например,
такая проблема…
• «Чистые» функции – данные программы
копируются в данные функции.
– Изменение состояния происходит через
возвращаемое значение в вызывающей функции.
– Данные между вызовами функций не сохраняются
• Если использовать указатели и ссылки, то можно
изменить данные переданных через такие
параметры.

39.

Композиция данных и функций
• Часть данных, которые нужно изменять
сгруппировать для удобства
– Data{D_1;
D_2;};
– int F_1 (int x,float y, char z, ссылка на экземпляр Data)
– int F_1 (int a,int b, ссылка на экземпляр Data)
• Тем не менее программисту надо будет
постоянно следить, чтобы не появились (или не
забылись) какие-то функции, изменяющие
данные, т.е. проблема совместного
использования данных

40.

Композиция данных и функций
• Идея: сгруппировать функции и связать их
с данными так, чтобы только функции из
этой композиции могли изменять данные
– Composition(A+D) -> Methods {
int F_1 (int x,float y, char z, ссылка на
экземпляр Data), int F_1 (int a,int b, ссылка на
экземпляр Data), экземпляр Data }

41.

Композиция данных и функций
Дополнительные идеи для реализации в языке:
• Для удобства не передавать каждый раз ссылку
на экземпляр
– Делегирование исполнения:
• M_1(int x,float y, char z)= { F_1(int x,float y, char z, Data&
theData }
• Как-то ограничить область действия данных
только этой композицией

42.

Композиция данных и функций:
реализация структуры с функциями в С++
struct Number
{
int ch;
nm sum(nm a)
{ nm rez;
rez.ch = a.ch + ch; return rez;}
nm sub(nm a)
{ nm rez; rez.ch = ch - a.ch;return rez;}
nm mult(nm a)
{
nm rez; rez.ch = ch * a.ch; return rez;}
nm divv(nm a)
{ nm rez; rez.ch = ch / a.ch; return rez;}
};

43.

Композиция данных и функций:
идея реализации
struct Number
{
int ch;
Number sum(Number a)
{ Number rez;
rez.ch = a.ch + ch;
};
Код сгенерированный
компилятором
Написанный Вами код
return rez;}
Специальное ключевое слово
для обращения к полям ЭТОЙ
структуры
struct Number
{
int ch;
Number sum(Number* this, nm a)
{Number rez;
rez.ch = a.ch + this->ch;
};
return rez;}

44.

Композиция данных и функций:
использование структуры «Число»
int main()
{
//создаём числа в автоматической памяти
Number x = { 1 };//иницализатор как для массивов
Number y ;
y.ch = 2;
cout <<"sum = " <<x.sum(y).ch <<"\n";//созали новое число, но да
сохранятся
cout <<"x = " <<x.ch<<endl;//старое значение не изменилось
//создаём числа в динаимческой памяти
Number* px, *py, z;
px = new Number({1});
py = new Number();
py->ch = 2;
z = px->sum(*py);
cout << "z = " <<z.ch << endl;

45.

Композиция данных и функций: структура
• Если несколько "связанных" данных?
• Компилятор сам создаёт структуру, в
которую включает (осуществляет
композицию) всех связанные данных, т.е.
сводим задачу к предыдущей и получаем
"замыкание".
– Активно используется в JavaScipt
– В С++ такая возможность реализована в лямбда
выражениях (о них в другой лекции)

46.

47.

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

48.

Перегрузка функций: постановка проблемы
• Задача:
• Реализовать набор функций, реализующих один и
тот же алгоритм для различных типов данных.
• В
C++
допускаются
перегруженные
имена
функций, когда функции с одним именем можно
идентифицировать по их списку параметров (т.е.
контексту, в котором имя употребляется).
• Использование нескольких функций с одним и тем
же именем, но с различными типами параметров,
называется перегрузкой (англ. resolution) функций.

49.

Перегрузка функций: примеры
• int Square(int arg)
{return arg*arg;}
• double Square(double arg)
{return arg*arg;}
• char *Square(const char *arg, int n)
{
static char res[256];
int j = 0;
while (*arg && j < n)
{
if (*arg != ' ') res[j++] = *arg;
res[j++] = *arg++;
}
res[j] = 0;
return res;
}

50.

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

51.

Перегрузка функций: идея для компилятора
• Декорирование имен – формирование
уникального внутреннего имени функции.
void Func(void); // @Func$qv
void Func(int); // @Func$qi
void Func(int, int); // @Func$qii
void Func(char*); // @Func$qpc
void Func(unsigned); // @Func$qui
void Func(const char*); // @Func$qpxc

52.

Перегрузка функций: поиск вариантов
• Если точного соответствия не найдено, выполняются
продвижения порядковых типов в соответствии с
общими
правилами
преобразования
типов:
bool → int, char → int, float → double и т. п.
• Далее выполняются стандартные преобразования
типов, например, int → double или указателей в void*.
• Далее выполняются преобразования типа, заданные
пользователем, а также поиск соответствий за счет
переменного числа аргументов функций.
• Если соответствие на одном и том же этапе может
быть получено более чем одним способом, вызов
считается неоднозначным и выдается сообщение об
ошибке.

53.

Перегрузка функций: неоднозначность
Неоднозначность может появиться при:
1. Преобразовании типа;
2. Использовании параметров-ссылок;
3. Использовании аргументов по умолчанию.
Правила описания перегруженных функций:
1. Нельзя перегружать функции, отличающиеся только
типом возвращаемого значения
2. Функции не могут быть перегружены, если описание
их параметров отличается только модификаторами
const, volatile или использованием ссылки (например,
int и const int или int и int&)

54.

Перегрузка функций: ошибки
int Sum(int x, int y)
{
return x + y;
}
int Sum(int number1, int number2)
{
return x + y;
}
void Sum(int x, int y)
{
Console.WriteLine(x + y);
}
Сигнатура у всех этих функций
будет совпадать:
Sum(int, int)

55.

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

56.

Перегрузка функций: особенности C++
• Константные ссылки: возможно: int& и const int&
void Func(const long& a) {
// a++;
cout << sizeof(a);
cout << "\t non const";
}
void Func(long& a) {
a++;
cout << sizeof(a); cout << "\t const";
}
int main(int argc, char* argv[]) {
const long xc = 1; long x = 1;
Func(x); cout << endl;
Func(xc); cout << endl;
cout << x << "\t" << xc;
return 0; }

57.

Перегрузка функций: другие ЯВУ (С#)
Для передачи типов значений по ссылке в С# были введены
специальные ключевые слова out и ref, которые учитываются
как значимые при перегрузке
void Increment(int val)
{
val++;
Console.WriteLine(val);
}
void Increment(ref int val)
{
val++;
Console.WriteLine(val);
}
ref указывает компилятору, что
передаваемый объект был
инициализирован до вызова функции
void Increment(out int val)
{
val++;
Console.WriteLine(val);
}
out сообщает, что переменная будет
инициализирована внутри функции

58.

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

59.

60.

Функция main
Функция main – та функция, с запуска которой начинает
работу консольное приложение, написанное на C++.
Форматы функции main:
void main(void);
int main(void);
void main(int argc, char *argv[]);
int main(int argc, char *argv[]);
// указаны традиционные имена для параметров
Первый и третий варианты не являются стандартными
и не работают на некоторых платформах. Кроме того,
писать void в скобках не обязательно. Таким образом,
самое простое и легальное объявление функции main
int main();

61.

Передача параметров в функцию main
Наличие параметров позволяет передать в
запускаемую программу данные командной строки:
пользователь может при запуске программы указать
несколько дополнительных параметров, разделяя их
пробелами.
int main(int argc, char *argv[]);
Аргумент argc определяет, сколько параметров,
включая имя программы, записано в командной строке.
Массив нуль-терминированных строк argv хранит
значение каждого параметра (argv[0] – имя exe-файла с
полным путем к нему, argv[1] – первый параметр и т.д.).

62.

Пример передачи параметров в функцию
main
Задача: в программу в качестве параметра передаётся
имя файла для обработки. Если параметр не передан,
программа должна запросить имя файла в диалоге.
int main(int argc, char *argv[]) {
char FileName[40];
setlocale(LC_ALL, ”.1251”);
if (argc>2) {
cout << ”Слишком много параметров\n”;
return 1;
}
if (argc==1) {
cout << ”Введите имя обрабатываемого файла: \n”;
cin.getline(FileName, 40);
}
else
strcpy(FileName, argv[1]);

}

63.

64.

65.

Перегрузка операции вызова функции
• Запись:
тип_возврата operator() (формальные параметры);
• Структура, в которой определён хотя бы один
оператор вызова функции, называется
функциональным классом. Функциональный класс
может не иметь других полей и методов.
• Функциональные классы используются в алгоритмах
из библиотеки STL.

66.

• Сквозной пример V

67.

• Работа с файлами и json
• https://www.youtube.com/watch?v=x8wsO
Nn1IcQ

68.

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

69.

70.

71.

ШАБЛОНЫ ФУНКЦИЙ
• Многие алгоритмы не зависят от типов данных, с
которыми они работают (классический пример —
сортировка).
• В С++ есть мощное средство параметризации —
шаблоны.
• С помощью шаблона функции можно определить
алгоритм, который будет применяться к данным
различных типов, а конкретный тип данных
передается функции в виде параметра на этапе
компиляции.

72.

ШАБЛОНЫ ФУНКЦИЙ
• Компилятор автоматически генерирует правильный
код, соответствующий переданному типу.
• Таким образом, создается функция, которая
автоматически перегружает сама себя и при этом
не содержит накладных расходов, связанных с
параметризацией.
• template < typename T >
• void sort( T array[], int size ); { /* тело функции */ }

73.

ШАБЛОНЫ ФУНКЦИЙ
• Первый же вызов функции, который использует
конкретный тип данных, приводит к созданию
компилятором
кода
для
соответствующей
версии функции. Этот процесс называется
инстанцированием шаблона (instantiation).
• Конкретный тип для инстанцирования либо
определяется компилятором автоматически,
исходя из типов параметров при вызове
функции, либо задается явным образом.
• При повторном вызове с тем же типом данных
код заново не генерируется.

74.

ШАБЛОНЫ ФУНКЦИЙ
// шаблон функции определения минимума
template < typename T >
T min( T a, T b )
{ return a < b ? a : b; }
// вызов функции по имени:
min( 1, 2 );
min( 'a', 'b' );
min( string( "abc" ), string( "cde" ) );

75.

ШАБЛОНЫ ФУНКЦИЙ
int i[5] = { 5, 4, 3, 2, 1 };
sort<int>( i, 5 );
char c[] = "бвгда";
sort<char>( c, strlen( c ) );
sort<int>( c, 5 );
// ошибка!

76.

ШАБЛОНЫ ФУНКЦИЙ
template< int BufferSize >
char* read()
{ char *Buffer = new char[ BufferSize ];
/* считывание данных */
return Buffer; }
• char *ReadString = read< 20 >();
• delete [] ReadString;
• ReadString = read< 30 >();

77.

ШАБЛОНЫ ФУНКЦИЙ
• // выведение значений параметров
int i[5] = { 5, 4, 3, 2, 1 };
sort( i, i + 5 );
// вызывается sort< int >
char c[] = "бвгда";
sort( c, c + strlen( c ) ); // вызывается sort< char >

78.

ОБЪЕДИНЕНИЯ
Объединения очень похожи на структуры, однако способ, с помощью
которого C++ хранит объединения, отличается от способа, с помощью
которого C++ хранит структуры.
union — это пользовательский тип, в котором все члены используют
одну область памяти.
Это означает, что в любой момент времени объединение не может
содержать больше одного объекта из списка своих членов.
Независимо от количества членов объединения, оно использует лишь
количество памяти, необходимое для хранения своего крупнейшего
члена.
Объединение состоит из частей, называемых элементами (членами).
Объединения могут быть полезны для экономии памяти при наличии
множества объектов и/или ограниченном количестве памяти. Однако
для их правильного использования требуется повышенное внимание,
поскольку нужно всегда сохранять уверенность, что используется
последний записанный элемент.

79.

80.

81.

Внутри программ объединения C++ очень похожи на
структуры. Например, следующая структура определяет
объединение с именем distance, содержащее два элемента:
union distance
{
int miles;
long meters;
};
Как и в случае со структурой, описание объединения
не распределяет память. Вместо этого описание
предоставляет шаблон для будущего объявления переменных.

82.

Следующая программа иллюстрирует использование объединения distance.
Сначала программа присваивает значение элементу
miles и выводит это значение. Затем программа присваивает значение
элементу meters. При этом значение элемента miles теряется:
#include <iostream.h>
void main(void)
{
union distance
{
int miles;
long meters;
} walk;
walk.miles = 5;
cout << «Пройденное расстояние в милях » << walk.miles << endl;
walk.meters = 10000;
cout << «Пройденное расстояние в метрах » << walk.meters << endl;
}
Программа обращается к элементам объединения с помощью точки,
аналогичная запись использовалась при обращении к элементам
структуры

83.

84.

Использование объединений позволяет экономит память:

85.

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

86.

Правила вычисления выражений
Для выполнения операций над некоторыми типами данных
требуется явное переопределение типов.
Различают:
Функциональное преобразование
<имя типа> (Список выражений)
Примеры:
int(3.14); float(2/3);
int(‘A’);
Однако, функциональная запись не подходит для сложного
типа.
В этом случае применяется каноническая форма
преобразования:
(имя типа)<выражение>
Примеры:
(unsigned long)(x/3+2); (long)25;(char)123;
Если ввести новый тип – тогда можно использовать и
функциональное преобразование
English     Русский Правила