new и delete. Умные указатели
Функции
malloc и
calloc не обеспечивают соответствия между размером выделенной памяти и типом данных.
double *p; //
...
p=calloc(n,sizeof(int)); //
Результат
calloc имеет тип
void *, который в C может быть преобразован к указателю любого типа без явного вызова операции преобразования, но даже использование явного преобразования результата не гарантирует, что в аргументах не будет ошибки:
p=(double *)calloc(n,sizeof(int));
Кроме того, многие программисты не заботятся о проверке результата функции на 0, считая, что памяти много.
Для устранения недостатков функций
malloc,
calloc и
free в С++ были введены операции
new и
delete.
Операция new возвращает
типизированный указатель, а после выделения памяти вызывается конструктор, который инициализирует память. Если память не может быть выделена, возникает исключительная ситуация
bad_alloc, т.е. проверять возвращаемое значение на 0 нет необходимости. Допускаются следующие формы для операции
new:
- new тип
- new тип()
- new тип[выражение]
- new тип[выражение]()
- new тип[выражение]{список выражений}
- new тип(список выражений) или new тип{список выражений}
3-я, 4-я и 5-я форма используется для создания массива объектов, остальные – для создания одиночных объектов. Различие между 1-й и 2-й, 3-й и 4-й формами проявляется только для стандартных типов данных и классов без конструкторов. В 1-м и 3-м случаях состояние объектов остается неопределенным, во 2-м и 4-м – инициализируется 0. Для классов с конструкторами в первых четырех формах вызывается конструктор по умолчанию. 5-я и 6-я форма предназначена для инициализации созданного объекта (массива объектов) с помощью конструктора с параметрами, а для стандартных типов данных можно указать только одно выражение, значение которого используется для инициализации созданного объекта.
Операция delete освобождает память, предварительно вызвав деструктор для объектов. Существуют две формы операции
delete:
- delete указатель
- delete[] указатель
1-я форма используется для уничтожения одиночного объекта, 2-я форма – массива объектов.
Примеры:
int *p;
p=new int(10);
...
delete p;
p=new int[10];
...
delete[] p;
Нельзя смешивать формы
new и
delete. Нельзя выделять память с помощью функций
malloc и
calloc, а освобождать с помощью
delete, или выделять с помощью
new, а освобождать с помощью
free даже для стандартных типов данных, не требующих вызова конструкторов и деструкторов, так как эти операции могут сохранять дополнительную информацию о выделенной памяти или использовать другую реализацию кучи.
Вместо указателей языка C рекомендуется использовать умные указатели:
- unique_ptr – уникальный, при уничтожении указателя удаляется и объект, при присваивании и копировании указателей передается владение объектом, а старый указатель обнуляется.
- shared_ptr – с подсчетом ссылок на объект, когда количество указателей на этот объект (или какую-то его часть) становится равно нулю, объект удаляется.
- weak_ptr – вспомогательный, не учитывается при подсчете ссылок, но с помощью метода expired позволяет обнаружить, что объект был удален.
У всех типов указателей перегружены операции -> и *(разыменования), а с помощью метода get можно получить обычный указатель.
struct data {int x; };
{ unique_ptr<data> p1(new data);
p1->x=1;
//
}
{ shared_ptr<data> p1(new data),p2;
weak_ptr<data> p3;
p2=p1;
p3=p1;
p1=nullptr;
p2=nullptr;
//
if(!p3.expired()) p3->x=1;
}
Для указателей на массивы, которые требуют специального деструктора, в качестве параметра шаблона нужно указать тип[]. Также вместо операций разыменования (
*) и
-> появляется операция индексации:
{ unique_ptr<int[]> arr{new int[10]};
arr[1]=10;
// автоматическое уничтожение объекта
}