Есть ли разница между:
operator delete(some_pointer);
и
delete some_pointer;
и если да, то в чем разница и где следует использовать одну и другую версию этого оператора? Спасибо.
Есть ли разница между:
operator delete(some_pointer);
и
delete some_pointer;
и если да, то в чем разница и где следует использовать одну и другую версию этого оператора? Спасибо.
Как ни странно, оператор delete
и operator delete()
- это не одно и то же.
delete some_pointer;
вызывает деструктор объекта, на который указывает some_pointer
, а затем вызывает operator delete()
, чтобы освободить память.
Обычно вы обычно не вызываете operator delete()
, потому что если вы это сделаете, деструктор объекта не будет вызван, и вы, скорее всего, закончите утечку памяти.
Единственный раз, когда вам нужно заботиться о operator delete()
, - это когда вы хотите выполнять собственное управление памятью, переопределяя operator new()
и operator delete()
.
Для этого вам также следует знать, что delete
и delete []
- две разные вещи.
operator delete()
просто освобождает память. delete some_pointer
вызывает some_pointer
деструктор, а затем вызывает operator delete()
.
delete some_pointer;
является "правильным" для использования.
operator delete(some_Pointer);
существуют главным образом как артефакт синтаксиса для определения вашего собственного оператора удаления. То есть, поскольку вы определяете оператор плюс как:
myclass::operator+(myclass b) {....}
вы действительно могли бы написать:
myclass c = a.operator+(b);
но никто никогда этого не делает. Они используют:
myclass c = a + b;
Аналогично, вы можете написать operator delete(some_Pointer);
, но никто никогда не делает.
По крайней мере, по моему опыту, более распространено реализовать operator new
и operator delete
, чем фактически использовать (т.е. вызывать) их, по крайней мере, напрямую.
Обычно вы косвенно используете operator new
и operator delete
- вы пишете new expression
, например A *a = new A;
. Чтобы реализовать это, компилятор генерирует код, который вызывает operator new
для выделения необработанной памяти, затем вызывает A::A
для преобразования этой необработанной памяти в объект A
, как если бы вы написали:
void *temp = operator new(sizeof A); // allocate raw memory with operator new
A *a = new(temp) A; // convert raw memory to object with placement new
Когда вы закончите с объектом, вы используете delete A;
. Чтобы реализовать это, компилятор вызывает dtor для объекта, а затем освобождает память, примерно так, как вы сделали:
a->~A();
operator delete(a);
Существуют также operator [] new
и operator [] delete
, которые используются, когда/если вы выделяете/удаляете массивы - но нет никакой реальной разницы между нормальной версией и версией массива - они оба просто выделяют заданное количество необработанной памяти (хотя вы можете догадаться, что версии массивов будут выделять относительно большие объемы памяти и сделать некоторую оптимизацию на этой основе).
В любом случае, если вы хотите оптимизировать распределение памяти для объектов определенного класса, вы перегружаете их для этого. Существует множество существующих реализаций, которые вы можете внедрять и использовать, особенно в ситуациях, когда вы планируете выделять большое количество мелких объектов, поэтому вам нужно минимизировать накладные расходы, связанные с каждым распределением (например, HeapLayers, Loki small block allocator).
Один интересный маленький лакомый кусочек: operator new
, operator [] new
, operator delete
и operator [] удалить are always
static class members, even if you don't explicitly include
static` в их объявлении/определении.
Существуют также глобальные версии всех четырех (::operator new
, ::operator [] new
, ::operator delete
и ::operator [] delete
). Они отмечают "границу" между "внутренним" управлением памятью С++ и внешним миром. Обычно они выделяют относительно большие куски памяти из операционной системы, а затем возвращают меньшие части в остальную часть программы по запросу. Если вы хотите (попытаться) оптимизировать управление памятью для всей вашей программы, вы обычно делаете это путем перегрузки (или, действительно, замены) этих. Опять же, типичной причиной может быть, если вы планируете выделить много мелких объектов (но не только в нескольких классах). Одним из примеров этого является библиотека Boost Pool.
Прямое использование любого из вышеперечисленного обычно ограничивается ситуациями, когда вам нужен блок необработанной памяти, а не объектов. Одним из примеров будет реализация ваших собственных классов контейнеров. Например, std::vector
обычно использует ::operator new
(через объект Allocator) для выделения памяти для хранения объектов. Поскольку он должен иметь возможность выделять хранилище, но только позже (или, возможно, никогда) не создавать объекты в этом хранилище, он не может просто использовать что-то вроде data = new T[size];
- он должен выделять необработанную память, а затем использовать новое место для создания объекты в памяти при добавлении их в коллекцию (например, когда вы push_back
объект). То же самое относится к std::deque
. Если бы вы хотели (например) реализовать свой собственный круговой буфер "с нуля", обратив все управление памятью напрямую, вместо того, чтобы использовать что-то вроде vector
для хранения, вам, вероятно, потребуется/захотеть сделать то же самое.