Дизайн классов с собственным выделением памяти

У меня возникли некоторые проблемы в разработке правильных классов, которые будут использовать собственное распределение памяти. Учти это:

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 *.

Есть ли лучший способ сделать это?

1

Решение

Другие решения

Трудно точно знать, что не так с вашим кодом, так как вы отказались от него. Лучшее, что я могу сделать, — показать вам рабочую программу.

Рассмотрим эту программу:

#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__

0

Первое, что забавно, это то, что вы не можете привести свой пустой указатель для вызова любой функции-члена. Технически это компилируется, но вызывается оператор 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 не будет вызываться.

0