Перегрузка операторов new и delete. Павел Синай — различия между версиями
Sinay (обсуждение | вклад) |
(→Возможные проблемы) |
||
(не показано 7 промежуточных версий 2 участников) | |||
Строка 21: | Строка 21: | ||
free(p); | free(p); | ||
} | } | ||
− | } | + | }; |
</source> | </source> | ||
+ | Эти операторы являются '''static'''. | ||
В оператор new передается параметр size, указывающий сколько памяти нужно выделить. Это сделано для возможности наследования от нашего класса - наследникам скорее всего понадобится больше памяти, и мы должны ее выделить. | В оператор new передается параметр size, указывающий сколько памяти нужно выделить. Это сделано для возможности наследования от нашего класса - наследникам скорее всего понадобится больше памяти, и мы должны ее выделить. | ||
− | Возвращаемое значение оператора new void*, а не A*, так как на момент его работы объект еще не существует. Конструктор будет вызван только после успешной отработки оператора new. | + | Возвращаемое значение оператора new '''void*''', а не '''A*''', так как на момент его работы объект еще не существует. Конструктор будет вызван только после успешной отработки оператора new. |
+ | |||
+ | Перегрузка операторов для создания и удаления массива объектов производится отдельно: | ||
+ | |||
+ | <source lang="cpp"> | ||
+ | void * operator new[](size_t size) | ||
+ | { | ||
+ | return malloc(size); | ||
+ | } | ||
+ | |||
+ | void operator delete[](void *p) | ||
+ | { | ||
+ | free(p); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | В оператор new передается размер блока памяти, предназначенного для хранения '''всех''' объектов в массиве. После выполнения оператора new[] производится последовательный вызов конструкторов для объектов из массива. | ||
+ | Перед выполнением оператора delete[] производится последовательный вызов деструкторов для всех объектов. | ||
+ | |||
+ | == Возможные проблемы == | ||
+ | <source lang="cpp"> | ||
+ | struct B : A | ||
+ | { | ||
+ | int b; | ||
+ | }; | ||
+ | |||
+ | B *pb = new B[10]; | ||
+ | A *pa = pb; //Ошибки не будет | ||
+ | |||
+ | delete [] pa; //Ошибка - пытаемся вызвать 10 раз деструктор | ||
+ | // для объекта класса A, но там объект класса B. | ||
+ | pa++; //Ошибка - указатель сдвинется на размер класса A, | ||
+ | // но поскольку в массиве лежат объекты класса B | ||
+ | // другого размера pa будет указывать не на | ||
+ | // следующий объект, а непонятно куда. | ||
+ | </source> | ||
+ | |||
== Перегрузка глобальных операторов new и delete == | == Перегрузка глобальных операторов new и delete == | ||
+ | Если перегрузить операторы вне класса, то будет произведена перегрузка всех соответствующих операторов, находящихся как внутри классов так и глобальных. | ||
+ | |||
+ | <source lang="cpp"> | ||
+ | void * operator new(size_t size) | ||
+ | { | ||
+ | return malloc(size); | ||
+ | } | ||
+ | |||
+ | void operator delete(void *p) | ||
+ | { | ||
+ | free(p); | ||
+ | } | ||
+ | </source> | ||
+ | |||
+ | Для перегрузки глобальных операторов также нужно перегружать отдельно операторы new и new[], delete и delete[]. | ||
+ | |||
+ | == Дополнительные параметры оператора new == | ||
+ | Обычно, при ошибке во время выполнения оператора new кидается исключение. Можно сделать так, чтобы исключение не кидалось, а вместо этого оператор возвращал NULL: | ||
+ | <source lang="cpp"> | ||
+ | int *pi = new(std::no_throw) int; | ||
+ | </source> | ||
+ | |||
+ | Для оператора delete такой возможности не предусмотрено, так как он в любом случае не кидает исключений. | ||
+ | |||
+ | Кроме того, можно создавать объект на уже выделенной памяти: | ||
+ | <source lang="cpp"> | ||
+ | int *A = new(pi)A(); | ||
+ | </source> | ||
+ | Эту операцию нужно производить осторожно - размера выделенной заранее памяти должно хватить для размещения объекта A. | ||
+ | В данном примере класс A содержит единственное поле int a, так что места для него будет достаточно. | ||
− | + | Возможные применения: | |
+ | * Работа при сильно ограниченной памяти. | ||
+ | * Необходимость создать объект по строго определенному адресу в памяти (например при работе со встраиваемой системой, если нужно создать объект для управления неким аппаратным ресурсом, расположенном по известному адресу). | ||
+ | * Реализация паттерна "Феникс". |
Текущая версия на 03:51, 11 июня 2012
Содержание
Для чего это нужно
- Организация кэширования памяти.
- Организация сборщика мусора.
- Поиск утечек памяти.
Перегрузка операторов new и delete в классах
Как это делается:
struct A
{
int a;
void * operator new(size_t size)
{
return malloc(size);
}
void operator delete(void *p)
{
free(p);
}
};
Эти операторы являются static.
В оператор new передается параметр size, указывающий сколько памяти нужно выделить. Это сделано для возможности наследования от нашего класса - наследникам скорее всего понадобится больше памяти, и мы должны ее выделить. Возвращаемое значение оператора new void*, а не A*, так как на момент его работы объект еще не существует. Конструктор будет вызван только после успешной отработки оператора new.
Перегрузка операторов для создания и удаления массива объектов производится отдельно:
void * operator new[](size_t size)
{
return malloc(size);
}
void operator delete[](void *p)
{
free(p);
}
В оператор new передается размер блока памяти, предназначенного для хранения всех объектов в массиве. После выполнения оператора new[] производится последовательный вызов конструкторов для объектов из массива. Перед выполнением оператора delete[] производится последовательный вызов деструкторов для всех объектов.
Возможные проблемы
struct B : A
{
int b;
};
B *pb = new B[10];
A *pa = pb; //Ошибки не будет
delete [] pa; //Ошибка - пытаемся вызвать 10 раз деструктор
// для объекта класса A, но там объект класса B.
pa++; //Ошибка - указатель сдвинется на размер класса A,
// но поскольку в массиве лежат объекты класса B
// другого размера pa будет указывать не на
// следующий объект, а непонятно куда.
Перегрузка глобальных операторов new и delete
Если перегрузить операторы вне класса, то будет произведена перегрузка всех соответствующих операторов, находящихся как внутри классов так и глобальных.
void * operator new(size_t size)
{
return malloc(size);
}
void operator delete(void *p)
{
free(p);
}
Для перегрузки глобальных операторов также нужно перегружать отдельно операторы new и new[], delete и delete[].
Дополнительные параметры оператора new
Обычно, при ошибке во время выполнения оператора new кидается исключение. Можно сделать так, чтобы исключение не кидалось, а вместо этого оператор возвращал NULL:
int *pi = new(std::no_throw) int;
Для оператора delete такой возможности не предусмотрено, так как он в любом случае не кидает исключений.
Кроме того, можно создавать объект на уже выделенной памяти:
int *A = new(pi)A();
Эту операцию нужно производить осторожно - размера выделенной заранее памяти должно хватить для размещения объекта A. В данном примере класс A содержит единственное поле int a, так что места для него будет достаточно.
Возможные применения:
- Работа при сильно ограниченной памяти.
- Необходимость создать объект по строго определенному адресу в памяти (например при работе со встраиваемой системой, если нужно создать объект для управления неким аппаратным ресурсом, расположенном по известному адресу).
- Реализация паттерна "Феникс".