Доброго времени суток, коллеги!!!
Такой нетиповой вопрос.
Есть сложная иерархия классов, наследуемых от одного чисто виртуального предка. Дерево наследования — произвольное, но сначала идут интерфейсы, а последний потомок любого дерева — имплементация. Имплементация содержит ссылку на кучу heap, или класс, который содержит в себе функции new и delete (будем называть их контекстом). Нужно удалить по ссылке на интерфейс имплементацию, но при этом удалить через контекст, в нужной куче или через нужные функции аллокации/деаллокации.
Примечание. Класс имеет право не пользоваться контекстом. Поэтому запихнуть ссылку на контекст в общий предок мы не можем. К тому же, если любой класс нашей системы будет иметь +4 или +8 байт на любой класс, даже инкапсулирующий int, это всё же значительная нагрузка на память.
У меня есть в голове несколько вариантов, жду ваших и критики моих. Есть чёткая цель — наиболее грамотный дизайн.
мои варианты:
1)
Скрытый член класса. переопределить new как-то так
class Object {
public:
virtual ~Object() = 0;
};
inline Object::~Object() {}
class HeapInterface {
public:
virtual void * Allocate(size_t number_bytes) = 0;
virtual void Deallocate (void * ptr) = 0;
virtual ~HeapInterface() {}
};
class Ansector1 : public Object {
public:
virtual ~Ansector1() {}
void* operator new (std::size_t size) {assert(0);return 0;}
void* operator new (std::size_t size, HeapInterface * context) {
const size_t kPtrSize = sizeof (HeapInterface*);
void * block = context -> Allocate(size+kPtrSize);
Ansector1 * ret = reinterpret_cast<Ansector1*>(static_cast<char *>(block) + kPtrSize);
HeapInterface ** heap_ptr = static_cast<HeapInterface **>(block);
heap_ptr[0] = context;
return ret;
}
void operator delete (void* ptr) {
HeapInterface * heap_value = static_cast<HeapInterface**>(ptr)[-1];
heap_value->Deallocate(static_cast<HeapInterface**>(ptr)-1);
}
HeapInterface * heap() {return reinterpret_cast<HeapInterface **>(this)[-1];}
void set_heap (HeapInterface * value) {reinterpret_cast<HeapInterface **>(this)[-1] = value;}
/*
* Недостатки:
* 1) нельзя использовать со стандартными контейнерами
* 2) непрозрачность извне (напишет кто-то перебор массива таких элементов с помощью указателей... и никогда не поймёт, где ошибка)
* 3) нерабочая арифметика указателей
*/
};
2)
Грязный хак в delete
class Ansector2 : public Object {
public:
Ansector2(HeapInterface * context_value) : heap_(context_value) {}
virtual ~Ansector2() {}
void operator delete (void* ptr) {
HeapInterface * heap_value = static_cast<Ansector2*>(ptr)->heap(); // грязный хак - обращение к члену класса после вызова деструктора класса
heap_value->Deallocate(ptr);
}
HeapInterface * heap() {return heap_;}
void set_heap (HeapInterface * value) {heap_ = value;}
private:
HeapInterface * heap_;
/*
* Недостатки:
* 1) использование грязного хака
*/
};
3)
Удаление через спецфункцию (с запретом на new и delete)
class Object3 {
public:
virtual void SelfDestruct() = 0;
virtual ~Object3() = 0;
};
inline Object3::~Object3() {}
class Ansector3 : public Object3 {
public:
Ansector3(HeapInterface * context_value) : heap_(context_value) {}
HeapInterface * heap() {return heap_;}
void set_heap (HeapInterface * value) {heap_ = value;}
virtual void SelfDestruct() {SelfDestruct(this);}
protected:
void operator delete (void* ptr) {} //ничего не делает. защищён, чтобы нельзя напрямую было вызывать Ansector3::new и Ansector3::delete
static Ansector3 * Create() {/* некий кодД */} //new использовать невозможно, но можно сделать фабричный метод (дофантазируйте его определение сами)
private:
static void SelfDestruct(Ansector3 * self) {
HeapInterface * heap_value = self->heap();
self->~Object3();
heap_value->Deallocate(self);
}
HeapInterface * heap_;
/*
* Недостатки:
* 1)удаление только через функцию
* 2)невозможность использовать некоторые стандартные фишки - например, стандартные умные указатели из разных библиотек, придётся писать свои
*/
};
4)
глобальный Map, связывающий адрес объекта и контекст. Функция delete по адресу объекта находит в глобальной мапе адрес объекта контекста и через него удаляет объект.
Недостатки:
1) потеря скорости удаления за счёт доступа к Map'у
2) увеличенная нагрузка на память
жду помощи, нужна самая грамотная архитектура из возможных, отдельно рассматриваются варианты на чистом c++98 и на c++11