管理带有预分配内存和数组的析构函数

Managing destructors with pre-allocated memory and arrays

本文关键字:数组 析构函数 内存 预分配 管理      更新时间:2023-10-16

你好,所以我正在尝试用预分配的内存创建对象和数组。例如,我有以下代码:

int * prealloc = (int*)malloc(sizeof(Test));
Test *arr = new(prealloc) Test();

其中test的定义如下:

class Test {
public:
    Test() {
        printf("In Constructorn");
    }
    ~Test() {
        printf("In Destructorn");
    }
    int val;
};

在这种情况下,如果我调用delete,它实际上会释放内存,这是坏的,b/c也许我正在使用某种类型的内存管理器,所以这肯定会导致一些问题。我在网上搜索了一下,我找到的唯一解决方案是显式调用析构函数,然后调用free:

arr->~Test();
free(arr);

还有别的方法吗?是否有一种方法可以调用delete并告诉它只调用析构函数而不释放内存?

我的第二个问题是在处理数组时,像前面的例子一样,您可以将预分配的内存传递给new:

int * prealloc2 = (int*)malloc(sizeof(Test) * 10);
Test *arr2 = new(prealloc2) Test[10];

如果我调用delete[],它不仅会调用数组中每个元素的析构函数,而且还会释放内存,这是我不想要的。我发现应该这样做的唯一方法是遍历数组并显式调用析构函数,然后调用free。就像常规的无数组操作符一样,有没有一种方法可以告诉操作符只调用析构函数而不释放内存?

我注意到的一件事是,数组的新操作符实际上会使用前4个字节来存储数组的大小(我只在visual studio中用32位构建测试了这一点),这将帮助我知道数组有多少元素,但仍然有一个问题。如果数组是指针数组呢?例如:

Test **arr2 = new Test*[10];

有人能帮我解决一下这些问题吗?

直接调用析构函数来销毁使用placement new创建的对象是正常的。至于其他方法,唯一明显的替代方法是使用Allocator对象(至少99%的情况下,它只是对位置new的包装,并直接调用析构函数)。

一般来说,根本不想使用new[]。您通常希望用operator new(或者可能是::operator new)分配原始内存,并将其与匹配的operator delete::operator delete一起释放。

在内存中使用new位置创建对象,并通过直接调用析构函数来销毁它们。

只能显式调用析构函数,因为delete也会尝试释放内存。

在你的代码中使用带有位置new的预分配内存应该是相当罕见的——一个典型的用例是当你处理直接内存映射的硬件接口时,当你想/需要在固定内存地址的顶部映射一个对象时——我通常认为这是一种代码气味。

如果您想为特定的类调整内存管理,您最好使用带有自定义分配器的STL容器,或者为特定的类重载new和delete操作符。

是的,这是唯一的方法。允许定义new而不允许定义delete是不对称的。[好吧,你可以做后者,但它只能在new抛出异常时被调用(下面没有正确处理!)

您可以使用模板化的destroy来实现相同的结果:

class Test 
{
public:
    Test() {
        printf("In Constructorn");
    }
    ~Test() {
        printf("In Destructorn");
    }
    int val;
};
class Allocator
{
public:
    static void* allocate(size_t amount) { return std::malloc(amount);}
    static void unallocate(void* mem) { std::free(mem);}
    static Allocator allocator;
};
Allocator Allocator::allocator;
inline void* operator new(size_t size,  const Allocator& allocator)
{
    return allocator.allocate(size);
}

template<class T>
void destroy(const Allocator& allocator, T* object) 
{ 
    object->~T();
    allocator.unallocate(object);
}


int main()
{
    Test* t = new (Allocator::allocator) Test();
    destroy(Allocator::allocator, t);
    return 0;
}