2010-08-22 3 views
1

Это вопрос дизайна, предполагающий C++ и иерархию ссылочных объектов. Множество классов в моей кодовой базе происходит от общего базового класса (ObjectBase), который реализует методы keep() и release() для увеличения и уменьшения количества ссылок экземпляра объекта.Ссылка на подсчитанные объекты и множественные распределители

Каждый экземпляр объекта может быть создан в стеке или в куче, используя ряд определяемых пользователем распределителей памяти. Чтобы экземпляр объекта совершил самоубийство (удалите это) в методе release(), если keepCount достигает 0, экземпляр должен знать, с каким распределителем он был создан.

В настоящее время я выделяю память для экземпляра объекта с помощью произвольного распределителя, а затем вызываю новое место для построения экземпляра объекта и вызываю метод setAllocator() объекта, чтобы установить создатель, с которым он был создан. Если объект был создан в стеке, распределитель установлен в NULL, а release() не будет вызывать удаление. Этот процесс очень избыточна и привести к ошибкам (утечки памяти, если я забыл позвонить setAllocator, и т.д. ...) В идеале я хотел бы сделать этот процесс один шаг, как это:

Object* o = myPoolAllocator.allocate<Object>(constructor arguments...); 

Но это делает очень сложно поддерживать и произвольное количество аргументов конструктора.

Я просто ищу идеи о том, как решить эту проблему. Мне очень нравится идея иметь возможность ссылаться на объекты подсчета, не полагаясь на умный указатель, тем более, что большинство классов вытекает из общей базы, так или иначе.

Благодарим за помощь.

Флориан

ответ

1

Посмотрите на эту статью: Overloading New in C++. Вы можете перегрузить new оператора для ObjectBase так, что он принимает ваш аллокатор в качестве параметра и делает остальную работу:

void *ObjectBase::operator new(size_t size, Allocator *allocator) { 
    void *ptr = allocator->allocate(size); 

    // Hack to pre-initialize member before constructor is called 
    ObjectBase *obj = static_cast<ObjectBase *>(ptr); 
    obj->setAllocator(allocator); 

    return ptr; 
} 

Обычно, оператор должен просто возвращает указатель на выделенную память, но так как вам нужен доступ к новому объекту для вызова метода setAllocator, я включил хак, который должен (но не обязательно) работать. Обратите внимание, что фактический конструктор ObjectBase вызывается после возврата указанной функции, поэтому вы должны убедиться, что ваш конструктор не повторно инициализирует член распределителя.

И тогда подобная перегрузка для delete:

void ObjectBase::operator delete(void *ptr) { 
    ObjectBase *obj = static_cast<ObjectBase *>(ptr); 
    obj->getAllocator()->free(ptr); 
} 

Вы бы затем создать объекты путем вызова new (allocator) SomeClass(...) где SomeClass происходит от ObjectBase.

Edit: Одна потенциальная проблема состоит в том, что вы не можете выделить объекты на стеке любых более, потому что нет никакого способа, чтобы инициализировать аллокатор к NULL, не влияя на том, как перегруженные new работы.

Обновление: Существует один последний (грязный) взлом, чтобы заставить его работать как с стеком, так и с динамическим распределением. Вы можете сделать new установить глобальную переменную (статический член класса тоже будет работать), указывающий на текущий распределитель, и конструктор может использовать это и сбросить его до NULL.В остальное время этот глобальный будет уже NULL, поэтому объект, построенный в стеке, получит распределитель NULL.

Allocator *currentAllocator = NULL; 

void *ObjectBase::operator new(size_t size, Allocator *allocator) { 
    currentAllocator = allocator; 
    return allocator->allocate(size); 
} 

ObjectBase::ObjectBase() { 
    setAllocator(currentAllocator); 
    currentAllocator = NULL; 
} 
+0

casablanca, спасибо за ваш быстрый ответ и спасибо за исправление исходного тега в моем OT! Я рассмотрел аналогичный метод, как вы предлагаете, но, как вы правильно отметили, я больше не буду создавать объекты в стеке. Я думал о проверке в конструкторе ObjectBase, независимо от того, указывает ли поле распределителя на действительный экземпляр распределителя, а если нет, учитывая, что объект находится в стеке. Однако это опасный бизнес, учитывая, что существует некоторая (хотя и небольшая) вероятность того, что неинициализированное поле распределителя просто указывает на действительный распределитель. – FlorianZ

+0

@FlorianZ: Смотрите мой обновленный ответ на еще один возможный взлом. – casablanca

+0

@casablanca. Это тоже неплохая идея! Моя единственная проблема заключается в том, что он, вероятно, не является потокобезопасным. Что делать, если контекстный переключатель возникает после возвращения нового оператора, но до того, как конструктор получит возможность потреблять переменную currentAllocator. Если во втором потоке выполняются распределения, то токАллактор будет скомпрометирован. Любые идеи относительно того, как сделать ваше решение потокобезопасным? – FlorianZ

Смежные вопросы