【问题标题】:Destructor called implicitly every time I allocate with placement new每次我分配新位置时都会隐式调用析构函数
【发布时间】:2017-06-01 11:38:46
【问题描述】:

我有以下问题:我在我的项目中使用自定义池分配器,每次我要分配带有新位置的任何对象时,也会隐式调用该对象的析构函数。

这里是源代码:

测试对象:

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);
}

池分配器:

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方法中return op后调用了析构函数!任何人都可以解释为什么会这样?

【问题讨论】:

  • 我想说你应该将调试打印添加到 all 构造函数。有些事情告诉我,你看到的额外的析构函数调用实际上是在临时发生的,可能在这里:Obj *obj1 = allocator::allocateNew(*poolAllocator, Obj("Hello"));

标签: c++ c++11 memory-management


【解决方案1】:

...每次我要分配带有新位置的任何对象时,也会隐式调用该对象的析构函数

不,析构函数也会在 an 对象上调用。您只是假设 - 错误地 - 被破坏的对象是在您的池中分配的对象。

直观地说,由于您的 allocateNew 函数将 something 的 const ref 作为参数,因此 something 必须存在,这意味着它是在分配任何内容之前创建的你的游泳池。

详细一点,

Obj *obj1 = allocator::allocateNew(*poolAllocator, Obj("Hello"));

将:

  1. Obj("Hello")

    使用std::string 构造函数创建一个新的匿名临时对象,该构造函数将记录您看到的消息

  2. allocateNew(Allocator&amp; allocator, const T&amp; t)

    将一个 const ref 传递给您的 allocateNew 函数。

  3. return new (...) T(t)

    placement-new another 池中的对象,使用隐式生成的复制构造函数不记录任何内容

  4. 返回指向池中分配的新对象的指针

  5. 在语句末尾超出范围时销毁匿名临时对象。

如果您想准确了解正在发生的事情,请实现日志版本所有构造函数(和赋值运算符)变体。

如果你想避免临时对象 + 复制,请将 T&amp;&amp; t 和 move-construct 传递到池中。

【讨论】:

    【解决方案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 表达式产生的对象上隐式调用析构函数。临时调用它。

    【讨论】:

      【解决方案3】:

      This: 'Obj("Hello")' 创建一个临时对象,用于复制初始化您的位置 new 中的另一个对象。之后通过调用其析构函数来销毁临时对象。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2021-10-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多