У меня возникли некоторые проблемы в разработке правильных классов, которые будут использовать собственное распределение памяти. Учти это:
class IAbstract { ... };
class CConcrete : public IAbstract { ... };
Я хочу сделать что-то вроде этого:
IAbstract *ptr = new CConcrete();
delete ptr;
Проблема в том, что я хочу, чтобы «новый» CConcrete использовал распределитель памяти. Кроме того, я хочу, чтобы «удалить» использовать собственный Deloclocator. Однако new и delete являются статическими функциями, поэтому удаление в приведенном выше примере не вызовет удаление CConcrete (как это должно быть, если удаление будет виртуальным).
Один из способов решить эту проблему — сделать что-то вроде этого:
class IAbstract {
public:
virtual Delete(void* ptr)=0;
void operator delete(void* ptr) {
((IAbstract*)(ptr))->Delete(ptr);
}
};
и переопределение Delete в производных классах. Но это решение довольно уродливо, особенно приведение ptr к IAbstract *.
Есть ли лучший способ сделать это?
Вы пробовали это? Место размещения новое / удалить
Трудно точно знать, что не так с вашим кодом, так как вы отказались от него. Лучшее, что я могу сделать, — показать вам рабочую программу.
Рассмотрим эту программу:
#include <iostream>
#define X() (std::cout << __PRETTY_FUNCTION__ << "\n")
class IAbstract {
public:
virtual ~IAbstract() { X(); }
};
class CConcrete : public IAbstract {
public:
void* operator new(size_t sz) {
X();
return ::operator new(sz); // or however you allocate memory
}
void operator delete(void* p) {
X();
::operator delete(p); // or however you de-allocate memory
}
~CConcrete() { X(); }
};
int main () {
IAbstract *ptr = new CConcrete();
delete ptr;
}
Вывод на моем компьютере такой:
static void* CConcrete::operator new(size_t)
virtual CConcrete::~CConcrete()
virtual IAbstract::~IAbstract()
static void CConcrete::operator delete(void*)
Обратите внимание, что когда delete ptr
выполняется, delete
правильно вызывает виртуальный деструктор а также правильный operator delete()
,
Примечание: это требует g ++ из-за использования __PRETTY_FUNCTION__
Первое, что забавно, это то, что вы не можете привести свой пустой указатель для вызова любой функции-члена. Технически это компилируется, но вызывается оператор delete после объект (или иерархия объектов) уничтожается (путем вызова деструктора (ов)). Другими словами — то, что вы делаете, это UB, поскольку вы пытаетесь получить доступ к vtable уничтоженного класса.
Другое дело, что пока ваш деструктор является виртуальным, в классе вызывается правильный оператор удаления. Поэтому нет необходимости заново изобретать свой собственный механизм, используя дополнительные ресурсы vtable. Я думаю, что вы ищете следующий подход:
#include <new>
#include <cstdio>
#include <cstdlib>
class Base {
public:
Base() { }
virtual ~Base() { printf("Base::~Base()\n"); }
};
class Derived : public Base {
char data[256];
public:
Derived() {}
virtual ~Derived() { printf("Derived::~Derived()\n"); }
void *operator new(size_t size)
{
void *p = malloc(size);
printf("Allocated %lu bytes @ %p\n", size, p);
return p;
}
void operator delete(void *ptr)
{
printf("Freeing %p\n", ptr);
free(ptr);
}
};
int main()
{
Base *b = new Base();
delete b;
b = new Derived();
delete b;
}
Но не забывайте, что как только вы удалите виртуальный деструктор, ваш перегруженный оператор delete не будет вызываться.