Операторы для динамического выделения и освобождения памяти (new и delete)

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

Для работы с динамической памятью в языке С использовались функции: calloc, malloc, realloc, free и другие. В С++ для выделения памяти можно также использовать встроенные операторы new и delete.

Оператор new имеет один операнд. Оператор имеет две формы записи:

[::] new [(список_аргументов)] имя_типа [(инициализирующее_значение)]

[::] new [(список_аргументов)] (имя_типа) [(инициализирующее_значение)]

В простейшем виде оператор new можно записать:

new имя_типа или new (имя_типа)

Оператор new возвращает указатель на объект типа имя_типа, для которого выполняется выделение памяти. Например:

char *str; // str – указатель на объект типа char

str=new char; // выделение памяти под объект типа char

или

str=new (char);

В качестве аргументов можно использовать как стандартные типы данных, так и определенные пользователем. В этом случае именем типа будет имя структуры или класса. Если память не может быть выделена, оператор new возвращает значение NULL.

Оператор new позволяет выделять память под массивы. Он возвращает указатель на первый элемент массива в квадратных скобках. Например:

int *n; // n – указатель на целое

n=new int[20]; // выделение памяти для массива

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

k=3;

int *p[]=new int[k][5]; // ошибка cannot convert from 'int (*)[5]' to 'int *[]'

int (*p)[]=new int[k][5]; // верно

При выделении памяти под объект его значение будет неопределенным. Однако объекту можно присвоить начальное значение.

int *a = new int (10234);

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

Class matr

{ int a; float b;

public:

matr(){}; // конструктор по умолчанию

matr(int i,float j): a(i),b(j) {}

~matr(){};

};

void main()

{ matr mt(3,.5);

matr *p1=new matr[2]; // верно р1 - указатель на 2 объекта

matr *p2=new matr[2] (2,3.4) ; // неверно, невозможна инициализация

matr *p3=new matr (2,3.4); // верно р3 – инициализированный объект

}

Следует отметить, что конструктор по умолчанию в примере требуется при использовании оператора new matr[2].

Использование знака :: перед оператором new указывает на вызов глобальной версии оператора new. Оператор new вызывает функцию operator new(). Аргумент имя_типа используется для автоматического вычисления размера памяти sizeof(имя_типа), то есть инструкция типа new имя_типа приводит к вызову функции:

operator new(sizeof(имя_типа));

Далее, в подразделе «Перегрузка операторов», будет рассмотрен случай использования доопределенного оператора new для некоторого класса. Доопределение оператора new позволяет расширить возможности выделения памяти для объектов (их компонент) данного класса.

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

При создании массива выполняется стандартный конструктор для каждого элемента.

Отметим преимущества использования оператора new перед использованием malloc():

- оператор new автоматически вычисляет размер необходимой памяти. Не требуется использование оператора sizeof(). При этом он предотвращает выделение неверного объема памяти;

- оператор new автоматически возвращает указатель требуемого типа (не требуется использование оператора преобразования типа);

- имеется возможность инициализации объекта;

- можно выполнить перегрузку оператора new (delete) глобально или по отношению к тому классу, в котором он используется.

Для разрушения объекта, созданного с помощью оператора new, необходимо использовать в программе оператор delete.

Оператор delete имеет две формы записи:

[::] delete переменная_указатель// для указателя на один элемент

[::] delete [] переменная_указатель// для указателя на массив

Единственный операнд в операторе delete должен быть указателем, возвращаемым оператором new. Если оператор delete применить к указателю, полученному не посредством оператора new, то результат будет непредсказуем.

Использование оператора delete вместо delete[] по отношению к указателю на массив может привести к логическим ошибкам. Таким образом, освобождать память, выделенную для массива, необходимо оператором delete [], а для отдельного элемента - оператором delete.

#include <iostream.h>

Class A

{ int i; // компонента-данное класса А

public:

A(){} // конструктор класса А

~A(){} // деструктор класса А

};

void main()

{ A *a,*b; // описание указателей на объект класса А

float *c,*d; // описание указателей на элементы типа float

a=new A; // выделение памяти для одного объекта класса А

b=new A[3]; // выделение памяти для массива объектов класса А

c=new float; // выделение памяти для одного элемента типа float

d=new float[4]; // выделение памяти для массива элементов типа float

delete a; // освобождение памяти, занимаемой одним объектом

delete [] b; // освобождение памяти, занимаемой массивом объектов

delete c; // освобождение памяти одного элемента типа float

delete [] d; // освобождение памяти массива элементов типа float

}

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

Наши рекомендации