引用计数对象和多个分配器 这是一个设计问题,假设C++和引用计数对象层次结构。我的代码库中的许多类都派生自一个公共基类(ObjectBase),它实现了retain()和release()方法来增加和减少对象实例的引用计数

引用计数对象和多个分配器 这是一个设计问题,假设C++和引用计数对象层次结构。我的代码库中的许多类都派生自一个公共基类(ObjectBase),它实现了retain()和release()方法来增加和减少对象实例的引用计数,c++,object,allocator,reference-counting,C++,Object,Allocator,Reference Counting,对象的每个实例都可以使用大量用户可定义的内存分配器在堆栈或堆上创建。为了使对象实例在retainCount达到0时在release()方法中自杀(删除此),实例必须知道它是用哪个分配器构造的 目前,我正在使用任意分配器为一个对象实例分配内存,然后调用placement new来构造该对象实例,并在该对象上调用setAllocator()方法来设置创建它时使用的分配器。如果对象是在堆栈上构造的,则分配器设置为NULL,release()将不调用delete。这个过程是非常冗余的,并且可能容易出错(

对象的每个实例都可以使用大量用户可定义的内存分配器在堆栈或堆上创建。为了使对象实例在retainCount达到0时在release()方法中自杀(删除此),实例必须知道它是用哪个分配器构造的

目前,我正在使用任意分配器为一个对象实例分配内存,然后调用placement new来构造该对象实例,并在该对象上调用setAllocator()方法来设置创建它时使用的分配器。如果对象是在堆栈上构造的,则分配器设置为NULL,release()将不调用delete。这个过程是非常冗余的,并且可能容易出错(内存泄漏,如果我忘记调用setAllocator等…),理想情况下,我希望将其作为一个单步过程,如下所示:

Object* o = myPoolAllocator.allocate<Object>(constructor arguments... );
Object*o=myPoolAllocator.allocate(构造函数参数…);
但这使得支持任意数量的构造函数参数变得非常困难

我只是想知道如何解决这个问题。我真的很喜欢能够引用count对象而不必依赖智能指针的想法,尤其是因为大多数类都是从公共基派生的

谢谢你的帮助


弗洛里安看看这篇文章:。您可以为
ObjectBase
重载
new
运算符,以便它将您的分配器作为一个参数,并执行其余的作业:

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;
}
然后通过调用
new(分配器)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;
}

请看这篇文章:。您可以为
ObjectBase
重载
new
运算符,以便它将您的分配器作为一个参数,并执行其余的作业:

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;
}
然后通过调用
new(分配器)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;
}

卡萨布兰卡,感谢您的快速响应,感谢您在我的OT中修复源标签!我已经考虑过类似的方法,如您所建议的,但正如您正确指出的,我将无法再在堆栈上创建对象。我考虑过检查ObjectBase构造函数,分配器字段是否指向有效的分配器实例,如果没有,则考虑对象在堆栈上。但是,考虑到未初始化的分配器字段恰好指向有效分配器的可能性(虽然很小),这是一项危险的工作。@FlorianZ:请参阅我的更新答案,以了解另一种可能的黑客行为。@casablanca。这也是个不错的主意!我唯一关心的是它可能不是线程安全的。如果上下文切换发生在new运算符返回之后,但在构造函数有机会使用currentlocator变量之前,该怎么办。如果分配随后在第二个线程中执行,那么currentAllocator将受到损害。关于如何使您的解决方案线程安全,有什么想法吗?为我自己关于线程安全的问题提出一个解决方案。我可以在分配currentAllocator之前使用互斥锁锁定CS,然后在ObjectBase构造函数中使用currentAllocator之后立即解锁同一互斥锁。有什么想法吗?@FlorianZ:我想说的是,你的程序是多线程的吗?您可以为全局线程使用线程本地存储,这将允许每个线程独立分配对象,而无需等待其他线程。不同的编译器(VC++和gcc)有自己指定线程本地修饰符的方法。casablanca,感谢您的快速响应,感谢您修复了my OT中的源代码标记!我已经考虑过类似的方法,如您所建议的,但正如您正确指出的,我将无法再在堆栈上创建对象。我考虑过检查ObjectBase构造函数,分配器字段是否指向有效的分配器实例,如果没有,则考虑对象在堆栈上。然而,考虑到未初始化的分配器字段j