Удаление полиморфного класса через контекст
От: Molchalnik  
Дата: 04.10.13 13:13
Оценка:
Доброго времени суток, коллеги!!!

Такой нетиповой вопрос.

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