Операция new используется не только для выделения памяти под отдельные объекты, но и под их массивы. Для этого после типа данных нужно указать в квадратных скобках размер массива:
переменная-указатель = new тип_данных[размер_массива];
Например, динамический массив целых чисел:
int *mas;
mas = new int[20];
Работа с таким массивом ничем не отличается от работы с обычным массивом, размер которого известен заранее.
Для освобождения массива используется операция delete[]:
delete[] mas;
Предостережение! Динамические массивы нельзя освобождать обычной операцией delete, т.к. поведение при этом определяется реализацией компилятора, но вряд ли оно будет правильным. Обычно при этом освобождается только первый элемент массива. Если же массив состоит из объектов, а не элементов простых типов, то деструкторы для этих объектов вызваны не будут.
Пример работы с одномерным динамическим массивом.
#include <iostream>
using namespace std;
void main()
{
setlocale(LC_ALL, "Russian");
size_t size;
unsigned long *arr;
cout << "Задайте размер массива: ";
cin >> size;
arr = new unsigned long[size];
for (size_t i = 0; i < size; i++)
arr[i] = (i == 0) ? 1 : arr[i-1]*i;
cout << "\nФакториалы чисел:\n";
for (size_t i = 0; i < size; i++)
cout << i << '\t' << arr[i] << '\n';
delete[] arr;
system("pause");
}
Для задания двумерного динамического массива нужно реализовать одномерный динамический массив, элементами которого будут одномерные динамические массивы, т.е. массив указателей, т.е. нужно сделать переменную типа указатель на указатель, например, так: int **mas.
Выделение и освобождение памяти непосредственно под многомерные массивы не предусмотрено, поэтому необходимо использовать те же операции new[] и delete[], что и для работы с одномерными массивами.
Выглядеть это будет примерно следующим образом:
int **mas;
int n, m;
cout << "Введите размеры двумерного массива\n";
cin >> n >> m;
mas = new int*[n];
for (int i = 0; i < n; i++)
mas[i] = new int[m];
// работа с массивом mas[n][m]
for (int i = 0; i < n; i++)
delete[] mas[i];
delete[] mas;
Проиллюстрировать процесс выделения памяти под массив можно схемой, показанной на рисунке 7.1. Сначала в переменной mas хранится указатель на указатель на int (int**). Первым делом мы выделяем память под одномерный массив, элементами которого будут указатели на int (int*). Затем для каждого элемента такого массива выделяется память под одномерные массивы типа int. Таким образом, получается двумерный массив элементов типа int, работать с которым можно точно так же, как с обычным статическим двумерным массивом.
Рисунок 7.1 – Выделение памяти под двумерный массив
Освобождение памяти производится в обратном порядке. Сначала в цикле освобождается память, выделенная под отдельные элементы, а затем – память, выделенная массиву типа int*.
Работа с динамическими массивами размерности большей двух выполняется аналогичным образом.
Ссылки
Ссылка (reference) тесно связана с указателями и устанавливает псевдоним объекта. Для объявления ссылки используется символ &. Например
short num = 50;
short &link = num; // обязательно инициализируем ссылку при объявлении
link += 50;
cout << num; // выведет 100
Ссылка по сути является постоянным указателем на объект. При инициализации в нее обязательно записывается адрес объекта, на который она будет ссылаться, а при каждом обращении к ней неявно применяется операция разыменования *.
В примере ссылка link инициализируется адресом переменной num. После этого можно сказать, что у значения, хранящегося в num, появляется новое имя (псевдоним). В результате изменение значения по ссылке равносильно изменению самой переменной, на которую указывает ссылка.
Если тип ссылки не совпадает с типом объекта, которым она инициализируются, то результат операции не определён и зависит от компилятора.
double d = 0.0;
int& ir = d; // попытка ссылку на int инициализировать объектом double
Некоторые компиляторы в этом случае создают новый анонимный объект, к которому привязывается ссылка, теряющая при этом свою функцию псевдонима. Другие компиляторы просто не позволяют такого поведения и при попытке инициализации вернут ошибку.
В описанных примерах используется так называемая независимая ссылка, т.е. новая переменная, являющаяся псевдонимом другой переменной. В этом качестве ссылки применяются нечасто, т.к. такое их использование редко оправдано и может привести к неочевидным и труднообнаруживаемым ошибкам.
Чаще всего ссылки используются для передачи в функции аргументов, значения которых должны быть изменены. Это так называемая «передача аргумента по ссылке» и она будет рассмотрена в следующей главе.