Есть ли разница в объявлении operator delete(void*)
и operator delete(void*, size_t)
для класса? Нужен ли на самом деле второй параметр? И если да, то зачем?
Наличие второго опционального параметра типа size_t
в функции operator delete
тянется еще со времен "классического" C++, т.е. C++98, где такой параметр мог использоваться в operator delete
, перегруженных именно для индивидуальных классов. То есть внутри конкретного класса вы можете на выбор объявлять
operator delete(void *p);
// или
operator delete(void *p, size_t s);
В ситуации, когда удаляемый класс предоставляет свой собственный перегруженный operator delete
со вторым параметром типа size_t
, компилятор обязан передать в такой оператор корректный размер удаляемого блока памяти, т.е. тот же размер, что передавался в operator new
при выделении этого блока.
Для глобального operator delete
такой возможности не предоставлялось.
Эта возможность предусмотрена в языке из-за того, что перегруженные operator new
и operator delete
базового класса могут использоваться для выделения/освобождения памяти для объектов производных классов
struct Base
{
void *operator new(size_t s)
{
std::cout << "new " << s << std::endl;
return ::operator new(s);
}
void operator delete(void *p, size_t s)
{
std::cout << "delete " << s << std::endl;
::operator delete(p);
}
};
struct Derived : Base
{
char buffer[1024];
};
int main()
{
Base *pb = new Base;
delete pb;
Derived *pd = new Derived;
delete pd;
}
При помощи анализа передаваемого в operator new
и operator delete
значения эти операторы могут определять, какой из возможных размеров выделяется или освобождается и соответствующим образом перенаправлять вызовы.
Для глобальных функций ::operator new
и ::operator delete
такой возможности в C++98 не было, т.е. глобально предоставлялся только замещаемый
operator delete(void *p);
Однако начиная с С++14 такая возможность была предоставлена и для глобальных функций тоже. Это было сделано для улучшения поддержки аллокаторов, которые не хранят размер блока в самом блоке (или где-то рядом), т.е. для аллокаторов которым трудно определить размер блока по указателю. Например, для пулов одноразмерных объектов. Также такая возможность может быть полезна для оптимизации выделения/освобождения памяти в появившейся в C++14 возможности расширенных выделений памяти, когда два или более соседних new-выражения обходятся одним вызовом operator new
с суммарным размером (и, соответственно, симметричной обработкой delete-выражений).
То есть если вы по какой-то причине хотите получать в ::operator delete
тот же самый размер, что передавался в соответствующий вызов ::operator new
, то объявляйте свой ::operator delete
со вторым параметром типа size_t
. Если вас не интересует этот размер, то объявляйте его без такого параметра.
Ситуация с наличием сразу двух вариантов operator delete
, похоже, находится в подвешенном состоянии уже давно и является дефектом стандарта. Скорее всего дело закончится тем, что предоставление сразу двух вариантов будет приводить к ошибке неоднозначности.
Оператор вызывается, если программист хочет вручную заниматься выделением памяти где-нибудь. (скрытно/в файлах/с обнулением) При вызове удаления объекта будет вызываться пользовательский delete
с аргументом размера памяти.
// > g++-5 -Wall -Wpedantic -std=c++14 operdelet.cpp
# include <iostream>
# include <string.h>
class A{
int i[10];
public:
void operator delete[](void * p,size_t s){
std::cout<<"A:s="<<s<<std::endl;
memset(p, 666,s); // зачищаю память
:: operator delete[]( p ); }
};
class B{
int i[100];
public:
virtual ~B(){}
void operator delete(void * p,size_t s);
};
class C:public B{
int i[1000];
public:
virtual ~C(){}
};
void B:: operator delete(void * p,size_t s){
std::cout<<"sizeof(B)="<<sizeof(B)<<std::endl;
std::cout<<"sizeof(C)="<<sizeof(C)<<std::endl;
std::cout<<"B:s="<<s<<std::endl;
memset(p, 555,s); // зачищаю память
:: operator delete( p ); }
void operator delete(void * p,size_t s){
std::cout<<"main:s="<<s<<std::endl;
memset(p, 777,s); // зачищаю память
:: operator delete( p ); }
int main() {
A * a = new A;
B * b = new B;
A * a7 = new A[7];
B * c = new C ;
delete c ;
delete [] a7 ;
delete b;
delete a; }
Сначала вызывается этот оператор, дальше я вызываю стандартное освобождение.
sizeof(B)=408
sizeof(C)=4408
B:s=4408
A:s=288
sizeof(B)=408
sizeof(C)=4408
B:s=408
main:s=40
Первый вызов удаления с виртуальным деструктором показывает, что оператор delete
из базового класса не знает какой родственный класс будет удалён, поэтому размер подскажет, что дальше делать.
Второе удаление массива классов показывает, что память выделенная под массив занимает семь элементов плюс размер массива (size_t). Оператор delete
не знает ни размера массива ни дополнительных проблем, что придумал данный компилятор. По-этому для ручного распределения памяти для массивов размер памяти очень важно знать.
Третье удаление это базовый класс, размер отличается от первого удаления.
Глобальный оператор delete
универсален для всех объектов, и размер нужно знать , сколько нужно освобождать.
Оборудование для ресторана: новинки профессиональной кухонной техники
Частный дом престарелых в Киеве: комфорт, забота и профессиональный уход
Обнаружилось, что на iOS устройствах в попап окне Fancybox 3 не кликабельны input поля, те
Добавляю совместимость интернет-магазина с Internet Explorer 8, для этого использую версию JQuery 110