Содержание:
Теория
- Шаблонная переменная объявляется началом класса или функции:
- Шаблонные переменные проверяются во время компиляции, что позволяет отлавливать ошибки перед запуском программы.
- Шаблонные функции определяют семейство функций.
- Объявлять их лучше в заголовочном файле, там, где и объявлены функции.
- Шаблонная функция сама по себе не является типом, или функцией, или какой-либо другой сущностью. Никакой код не генерируется из исходного файла, содержащего только определения шаблонов. Для того чтобы появился какой-либо код, должен быть создан экземпляр шаблона: аргументы шаблона должны быть определены таким образом, чтобы компилятор мог сгенерировать функцию.
- Явное определение экземпляра принудительно создает экземпляр функции или функции-члена, на которые они ссылаются. Он может появиться в программе в любом месте после определения шаблона, и для данного списка аргументов разрешается появляться в программе только один раз, диагностика не требуется.
- Неявное определение экземпляра: Когда код ссылается на функцию в контексте, который требует существования определения функции, и эта конкретная функция не была создана явно, происходит неявное создание экземпляра. Список аргументов шаблона указывать не обязательно, если он может быть выведен из контекста.
- What to do?:
- Why it’s not enouph?:
- Error 2. We returned dynamic memory twice using delete
- Error 3. Dynamic memory leak
- Error 3a. Dynamic memory leak in the loop
- Error 3b. Dynamic memory leak in the function.
- The rule. If dynamic memory is allocated in a function, then it must be returned in the same function. There are exceptions.
- A two-dimensional array is considered as a one-dimensional array of one-dimensional arrays
- Two-dimensional rectangular arrays («matrices») are conveniently modeled using pointer arrays:
- The reference to the (i, j)-th element of the matrix looks like this:
mat[i][j]
. To iterate over the matrix, just as in the case of a regular array, you can use pointers. - It is necessary to empty (devastate) memory, it should be done in the reverse order: first, the memory for each row is released in the loop:
delete [] mat[i]
. And then the memory of the array of pointersmat
:delete [] mat
is released. This procedure should be executed as a function with one argumentrows
(number of rows). - This is an array of three elements of type
int [4]
- In order to pass a two-dimensional array of arbitrary dimension to a function, it should be allocated in dynamic memory:
- After using it in the function, the allocated memory must be returned:
Шаблонные функции
template <typename T> int max(T a, T b) { if (a > b) { return a; } return b; } |
#include <iostream> using namespace std; template <typename T> T max(T a, T b) { if (a > b) { return a; } return b; } int main() { cout << "max(3, 5): " << max(3, 5) << endl; // 5 cout << "max('a', 'd'): " << max('a', 'd') << endl; // d cout << "max(\"Hello\", \"World\"): " << max("Hello", "World") << endl; // Hello cout << "max(3, 5, 6): " << max(3, 5, 6) << endl; // error return 0; } |
lib.cpp
#include "lib.h" |
lib.h
template<typename T> class My { My() { } T f(T a, T b); }; template<typename T> T My::f(T a, T b) { return a + b; } |
template<typename T> void f(T s) { std::cout << s << '\n'; } template void f<double>(double); // создает экземпляр f<double>(double) template void f<>(char); //создает экземпляр f<char>(char) template void f(int); // создает экземпляр f<int>(int) |
#include <iostream> template<typename T> void f(T s) { std::cout << s << '\n'; } int main() { f<double>(1); // создает экземпляр и вызывает f<double>(double) f<>('a'); // создает экземпляры и вызывает f<char>(char) f(7); // создает экземпляры и вызывает f<int>(int) void (*ptr)(std::string) = f; // } |
1. Указатель на одномерный массив в динамической памяти
int* p = new int[10]; for (int* q = p; q<p+10; q++) *q = 1; for (int i=0; i<10; i++) p[i] = 1; delete [] p; |
Для простых типов можно написать delete p (подсистема знает, что мы выделили массив), но для классов это ошибка.
Error 1. Accessing a section of dynamic memory that we have already returned using delete
int* p = new int; *p = 777; delete p; *p = 666; |
int* p = new int; *p = 777; delete p; p = nullptr; // ! *p = 666; |
int* p = new int; int * q = p; … delete p; p = nullptr; // ! *q = 666; |
int* p = new int; *p = 777; delete p; … delete p; |
int* p = new int[1000]; *p = 777; … // and forgot to delete! |
for (int i = 0; i<100000; i++) { int* p = new int[1000]; *p = 777; … // and forgot to delete! } |
void p() { int* p = new int[1000]; *p = 777; … // and forgot to delete! } |
Two-dimensional arrays
int a[3][4]; |
int ** mat = new int *[rows]; // future matrix of rows of rows |
Each element mat[i]
of this array is a pointer, it should be initialized in a loop, assigning it the result new int [cols]
, where cols
is the desired number of columns of the matrix.
Another example:
a[i][j] ~ *(a[i] + j) ~ *(*(a + i) + j) |
Printing an array:
void print(int a[][4], int m, int n) { for (int i=0; i<m; i++) { for (int j=0; j<n; j++) cout << a[i][j] << " "; cout << endl; } } |
Two-dimentional arrays and dynamic memory
void print(int** a, int m, int n) { for (int i=0; i<m; i++) { for (int j=0; j<n; j++) cout << a[i][j]; // a[i][j] ~ *(*(a + i) + j) cout << endl; } } int main() { // allocation in memory int** p = new int*[3]; // pointer pointing to pointer (3 rows) for (int i=0; i<3; i++) p[i] = new int[4]; // 4 columns print(p,3,4); … } |
int main() { int** p = new int*[3]; for (int i=0; i<3; i++) p[i] = new int[4]; print(p,3,4); // Нарисовать на доске // deallocation of memory: for (int i=0; i<3; i++) delete [] p[i]; delete [] p; p = nullptr; } |
Stepwise two-dimensional dynamic arrays
int main() { int** p = new int*[3]; for (int i=0; i<3; i++) p[i] = new int[4]; print(p,3,4); for (int i=0; i<3; i++) delete [] p[i]; delete [] p; p = nullptr; } |
Лабораторные работы
1. Одномерные массивы в динамической памяти
Выполнить: Создайте приложение с двумя шаблонными функциями. Одна из них запрашивает элементы одномерного массива заданного размера, другая — выводит элементы одномерного массива заданного размера.
Указание: Заголовки функций должны быть следующими:
template <typename T> void inputArr(T* arr, int &size); template <typename T> void printArr(const T* arr, int &size); |
Примерный вывод:
Lab 1: Array[0] 1.1 Array[1] 2.1 Array[2] 3.2 Array[3] 4.1 Array[4] 5.4 1.1 2.1 3.2 4.1 5.4
✍ Алгоритм:
- Над функцией
main()
добавьте код шаблонной функции для ввода элементов массива. Функция будет иметь два параметра — указатель на массив и ссылочную переменную, хранящую размер массива. - В основной функции инициализируем переменную, хранящую размер массива (массив будет состоять из 5 элементов), и объявим указатель на новый массив типа
double
: - Вызовите функцию:
- Над функцией
main()
добавьте код шаблонной функции для вывода элементов массива: - Вызовите функцию:
- Раз для динамического массива была выделена память, то ее необходимо освободить:
- Запустите программу.
template <typename T> void inputArr(T* arr, int &size) { // arr - указатель на массив любого типа for (int i = 0; i < size; i++) { cout << "Array[" << i << "] "; cin >> arr[i]; // разыменование указателя } }
int size = 5; double* myArray = new double[size];
inputArr(myArray, size);
template <typename T> void printArr(const T* arr, int &size) { for (int i = 0; i < size; i++) { //cout << arr[i] << " "; // разыменование способ 1: cout << *(arr + i) << " "; //разыменование способ 2: } }
printArr(myArray, size);
delete[]myArray; myArray = NULL;
Выполнить: Дан массив целых чисел (A
). Создайте массив, скопировав в него все элементы, но вместо элементов, последняя цифра которых равна 0, необходимо вставить 0. В этой задаче необходимо использовать динамическую память (new
/ delete
). Создайте шаблонную функцию для вывода массива.
Указание 1: Убедитесь, что в основной программе после вывода результирующего массива память была освобождена (delete []
).
Указание 2: Заголовок функции:
void MakeArrayWithZero(int* a, int& size); |
Где a
- массив, а size
- размер массива.
Указание 3: Шаблонная функция для вывода массива должна быть объявлена в заголовочном файле:
template<typename T> void printArray(T const * arr, int size, char delim = ' ') { ... } |
Примерный вывод:
Task 1: Исходный массив: 1 20 30 4 5 Результирующий массив: 1 0 0 4 5
[Solution and Project name: Lesson_8task1
, file name L8Task1main.cpp
, L8Task1imp.cpp
, L8Task1.h
]
Выполнить: Дан целочисленный массив. Создать новый массив, в котором будут удалены из исходного массива все вхождения заданного числа. Исходный массив не изменяется. Не следует использовать какие-либо стандартные функции.
Указание 1: Сначала необходимо создать новый массив с указанным количеством элементов, как в исходном массиве. Для этого используйте функцию со следующим заголовком:
int* delete_all_entries(int* a, int size, int n, int& new_size) |
Указание 2: Создайте шаблонную функцию для печати элементов массива. Используйте следующий заголовок функции:
template<typename T> void print_array(T const* a, int size, char delim = ' '); |
Примерный вывод:
Задание 2: исходный массив: 1 2 3 2 число, которое следует удалить: 2 новый массив: 1 3
[Solution and Project name: Lesson_8task2
, file name L8Task2main.cpp
, L8Task2imp.cpp
, L8Task2.h
]
Двумерные массивы
Выполнить: Определить функцию для создания матрицы заданных размеров, все элементы которой равны заданному числу::
заголовочный файл:
int ** createMatrix(int rows, int cols, int value = 0); |
The default value of 0
for the value
parameter should be specified only in the header file. In the cpp file, the header contains three parameters in the usual form:
implementation file:
int ** createMatrix(int rows, int cols, int value) { } |
Значение по умолчанию равное 0
для параметра value
должно быть указано только в заголовочном файле. В cpp файле заголовок функции содержит три параметра в обычной форме:
Указание : Для вывода элементов матрицы следует создать еще одну функцию с заголовком:
void printMatrix(int ** mat, int rows, int cols); |
Примерный вывод:
Lab 2: matrix 5 by 5: 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
[Solution and Project name: Lesson_8Lab2
, file name L8Lab2main.cpp
, L8Lab2imp.cpp
, L8Lab2.h
]
✍ Алгоритм:
- Создайте приложение и добавьте необходимые директивы.
- Поскольку необходимо создать матрицу в функции, откройте заголовочный файл и добавьте сигнатуру функции:
- Затем откройте файл реализации и добавьте функцию:
- Прежде всего, необходимо выделить память для двумерного массива (матрицы). Для этого добавьте код в функцию:
- Функция возвращает матрицу:
- Теперь перейдем в функцию
main
для вызова созданной нами функции. - Поскольку функция вернет созданную матрицу, необходимо создать переменную для хранения этой матрицы и присвоить ей результат вызова функции.
- Попробуйте запустить программу и убедитесь, что ошибок нет.
- Далее необходимо создать функцию для вывода элементов матрицы. Там будет три аргумента - сама матрица, количество столбцов и строк. Добавьте код в файл реализации:
- Добавьте заголовок функции в заголовочный файл. Сделайте это самостоятельно.
- Запустите программу и убедитесь в отсутствии ошибок.
- После создания динамического массива мы должны освободить память. Создадим функцию для освобождения памяти. Добавьте код функции:
- Добавьте заголовок функции в заголовочный файл и вызовите функцию в основном исходном файле.
#pragma once #ifndef HEADER_H #define HEADER_H int ** createMatrix(int rows, int cols, int value = 0); #endif
// ... int ** createMatrix(int rows, int cols, int value) { //... }
int** res = new int*[rows]; for (int i = 0; i < rows; i++) { res[i] = new int[cols]; for (int j = 0; j < cols; j++) { res[i][j] = value; } }
В строке #4 для каждой строки выделяется память для столбцов. Количество строк и столбцов берем из значений аргументов.
return res; } // конец функции
int main() { // матрица 4 х 5 , каждый элемент = 0: int** matrix = createMatrix(4, 5, 0); //... }
void printMatrix(int ** mat, int rows, int cols) { for (int i = 0; i < rows; i++) { for (int j = 0; j < cols; j++) { cout << mat[i][j] << " "; } cout << endl; } }
void freeMemory(int ** mat, int rows) { for (int i = 0; i < rows; i++) { delete[] mat[i]; } delete[] mat; mat = NULL; }
Алгоритм: Создайте функцию для нахождения минимального и максимального значений матрицы. Для выполнения задания можно использовать аналогичные функции из лабораторной работы №2 (создать матрицу, вывести ее и освободить память). Элементы матрицы должны быть сгенерированы случайным образом. Заголовок функции для нахождения min
и max
должен быть следующим:
void matrixMinMax(int ** mat, int rows, int cols, int &min, int &max); |
Указание: для генерации случайных элементов, нужно подключить следующую библиотеку:
#include <ctime> // ... srand(time(0)); //.. в цикле: array_name[i][j] = 1 + rand() % 10; |
Примерный вывод:
Task 3: matrix 4 by 5: 5 4 0 7 0 0 8 0 1 2 0 5 0 7 0 4 0 0 9 0 max = 9 min = 0
[Solution and Project name: Lesson_8task3
, file name L8Task3main.cpp
, L8Task3imp.cpp
, L8Task3.h
]
Выполнить: Дана квадратная матрица из целочисленных элементов. Вычислите сумму элементов ее главной диагонали. Убедитесь, что в основной программе после работы с матрицей, соответствующая память освобождается.
Примерный вывод:
Task 4: matrix: 8 3 3 6 6 4 9 8 6 1 9 6 4 5 1 5 sum = 26
[Solution and Project name: Lesson_8task4
, file name L8Task4main.cpp
, L8Task4imp.cpp
, L8Task4.h
]