我有以下问题:我正在使用我的项目自定义池分配器,每次我要分配任何带有placement new的对象时,析构函数也会隐式调用该对象。
以下是源代码:
测试对象:
class Obj {
public:
Obj(const std::string& s) {
std::cout << "Constructor Called" << std::endl;
}
~Obj() {
std::cout << "Destructor Called" << std::endl;
}
};
主:
int main()
{
void *pmemory;
pmemory = malloc(ONEGIG_SIZE);
PoolAllocator* poolAllocator = new PoolAllocator(sizeof(Obj), __alignof(Obj), ONEGIG_SIZE, pmemory);
Obj *obj1 = allocator::allocateNew(*poolAllocator, Obj("Hello")); //<-- const and dest is called
//......
return 0;
}
这是Allocator的源代码: AllocateNew功能:
template <class T> T* allocateNew(Allocator& allocator, const T& t)
{
return new (allocator.allocate(sizeof(T), __alignof(T))) T(t);
}
Pool Allocator:
PoolAllocator::PoolAllocator(size_t objectSize, u8 objectAlignment, size_t size, void* mem)
: Allocator(size, mem), _objectSize(objectSize), _objectAlignment(objectAlignment)
{
ASSERT(objectSize >= sizeof(void*));
//Calculate adjustment needed to keep object correctly aligned
u8 adjustment = pointer_math::alignForwardAdjustment(mem, objectAlignment);
_free_list = (void**)pointer_math::add(mem, adjustment);
size_t numObjects = (size-adjustment)/objectSize;
void** p = _free_list;
//Initialize free blocks list
for(size_t i = 0; i < numObjects-1; i++)
{
*p = pointer_math::add(p, objectSize );
p = (void**) *p;
}
*p = nullptr;
}
PoolAllocator::~PoolAllocator()
{
_free_list = nullptr;
}
void* PoolAllocator::allocate(size_t size, u8 alignment)
{
ASSERT(size == _objectSize && alignment == _objectAlignment);
if(_free_list == nullptr)
return nullptr;
void* p = _free_list;
_free_list = (void**)(*_free_list);
_used_memory += size;
_num_allocations++;
return p;
}
void PoolAllocator::deallocate(void* p)
{
*((void**)p) = _free_list;
_free_list = (void**)p;
_used_memory -= _objectSize;
_num_allocations--;
}
似乎在Pool Allocator中的allocate方法返回后调用析构函数!任何人都可以解释为什么会这样?
答案 0 :(得分:5)
...每次我要使用placement new分配任何对象时,析构函数也会隐式调用该对象
不,析构函数也在 对象上调用。您只是假设 - 错误地 - 被破坏的对象是您在池中分配的对象。
直观地说,由于你的allocateNew
函数将const ref作为某事作为参数,所以某些东西必须存在,这意味着它是在任何东西之前创建的。在你的游泳池中分配。
详细说明,
Obj *obj1 = allocator::allocateNew(*poolAllocator, Obj("Hello"));
意愿:
Obj("Hello")
使用std::string
构造函数创建一个新的匿名临时对象,该构造函数将记录您看到的消息
allocateNew(Allocator& allocator, const T& t)
将const ref传递给您的allocateNew
函数。
return new (...) T(t)
使用隐式生成的复制构造函数,在池中放置新的另一个对象
返回指向池中分配的新对象的指针
在声明结尾处超出范围时销毁匿名临时。
如果您想要准确了解正在发生的事情,请实施日志记录版本所有构造函数(和赋值运算符)变体。
如果您想避开临时对象+副本,请将T&& t
和move-construct传递到池中。
答案 1 :(得分:2)
声明
Obj *obj1 = allocator::allocateNew(*poolAllocator, Obj("Hello"));
创建一个Obj
类型的临时文件(来自Obj("Hello")
),并通过引用将其传递给allocator::allocateNew()
。
在函数
中template <class T> T* allocateNew(Allocator& allocator, const T& t)
{
return new (allocator.allocate(sizeof(T), __alignof(T))) T(t);
}
将该临时对象作为t
接收,new (allocator.allocate(sizeof(T), __alignof(T))) T(t)
创建它的副本(在allocator.allocate()
提供的内存中使用提供的参数。这使用了复制构造函数,代码没有跟踪。
当allocator::allocateNew()
返回时,在控制传递给下一个语句之前,临时值将被销毁。
allocateNew()
内创建的副本仍然存在。
不会在new
表达式导致的对象上隐式调用析构函数。它被称为临时的。
答案 2 :(得分:1)
这个:'Obj(“Hello”)'创建一个临时用于复制初始化你的贴牌新对象的另一个对象。之后,通过调用析构函数来销毁临时文件。