这是一个设计问题,假设C ++和引用计算了对象层次结构。我的代码库中的很多类派生自一个公共基类(ObjectBase),它实现了retain()和release()方法来增加和减少对象实例的引用计数。
可以使用许多用户可定义的内存分配器在堆栈或堆上创建对象的每个实例。为了使对象实例在release()方法中自动(删除它),如果retainCount达到0,则实例必须知道它已经构造了哪个分配器。
目前,我正在使用任意分配器为对象实例分配内存,然后调用placement new来构造对象实例,并在对象上调用setAllocator()方法来设置它已创建的分配器。如果对象已在堆栈上构造,则分配器设置为NULL,release()不会调用delete。这个过程是非常多余的,并且可能容易出错(内存泄漏,如果我忘记调用setAllocator等等)。理想情况下,我希望这样做一个一步过程:
Object* o = myPoolAllocator.allocate<Object>(constructor arguments... );
但这使得支持和任意数量的构造函数参数变得非常困难。
我只是在寻找如何解决这个问题的想法。我非常喜欢能够在不必依赖智能指针的情况下引用计数对象的想法,特别是因为大多数类来自公共基础,无论如何。
感谢您的帮助。
Florian
答案 0 :(得分: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
方法,所以我已经包含了一个应该(但可能不是)的hack工作。请注意,在上面的函数返回后调用实际的ObjectBase
构造函数,因此您应该确保构造函数不会重新初始化allocator成员。
然后delete
的类似重载:
void ObjectBase::operator delete(void *ptr) {
ObjectBase *obj = static_cast<ObjectBase *>(ptr);
obj->getAllocator()->free(ptr);
}
然后,您可以通过调用new (allocator) SomeClass(...)
从SomeClass
派生的ObjectBase
来创建对象。
编辑:这个问题的一个潜在问题是您无法再在堆栈上分配对象,因为无法将分配器初始化为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;
}