在自定义分配器中处理没有默认构造函数

时间:2012-04-26 17:31:52

标签: c++ design-patterns memory memory-management constructor

我的new宏有#define,要使用我自己的分配器,例如MYNEW(Type, Allocator),我将使用malloc分配一些原始内存,然后使用放置new用于在原始内存上分配类型,例如

#define MYNEW(Type, Allocator) Allocator->Alloc<Type>(sizeof(Type));`
template<typename Type>
Type* Alloc(unsigned int size) // Allocator::Alloc<Type>
{
    return (Type*)new(malloc(reportedSize)) Type;
}

但是,当Type没有默认构造函数时,我遇到了问题。我尝试过的一个场景是做MYNEW_1(Type, Allocator, ConVar1)之类的事情,其中​​会传递ConVar1,例如

#define MYNEW_1(Type, Allocator, ConVar1) Allocator->Alloc<Type>(sizeof(Type), ConVar1);`
template<typename Type, typename ConVarType>
Type* Alloc(unsigned int size, ConVarType Convar1) // Allocator::Alloc<Type>
{
    return (Type*)new(malloc(reportedSize)) Type(Convar1);
}

这种方法的问题是,对于我的自定义Vector,我也使用MYNEW来分配内存。但是,对于我的Vector使用的一些Type,没有默认构造函数,我无法分辨构造函数可能需要多少变量。

有没有人对如何解决这个问题有任何见解? (当然没有说使用std :: types而不是我自己的。我这样做是为了了解更多)。我不希望重载operator new,因为我在那里有内存跟踪,因为我不希望内存跟踪两次(Alloc有更多的内部跟踪,但我所展示的是简化示例)而宁愿使用malloc。

2 个答案:

答案 0 :(得分:4)

看起来您不仅要做自己的自定义分配器,而且还不一定要尝试定义兼容STL的分配器。但是,我认为识别出所有的错误是有用的,STL分配器是为处理这种情况而设计的。 Allocationconstruction分开:

  

请注意,[allocator::construct]不为元素分配空间,它应该已在p处可用(请参阅成员allocate以分配空间)。

     

相当于[复制现有对象的[{1}}调用]。

一般来说,我相信你有两个选择:(1)重载new来处理分配内存并让系统处理构造,或者(2)使你的分配器接口更像STL分配器接口。


“重载operator new”通常用于表示两件事:(1)替换它,或(2)添加新版本的operator new。当我最初回答时,我使用的是第一个含义(实际上应该称为“替换new”或“覆盖operator new”;但是考虑到这一点,我已经确定真正超载operator new会满足您的要求。

我应该提一下,调用重载operator new的语法是如此糟糕,以至于许多人仅仅出于这个原因就避免使用这种技术。您可以创建一个函数(例如operator delete)并使用它,而不是重载delete

这种方法的价值在于您遵循将内存分配与对象构造分离的语言标准,并将其留给编译器来为您处理对象构造。您不必担心转发运算符,或者没有默认构造函数的类或任何这些问题。

重载deallocate / operator new(当然,我依靠你来充实operator deletetrack):

release

重载#include <new> #include <cstdlib> struct Allocator { void track(void* p, const void* container) const; void release(void* p, const void* container) const; }; void* operator new (size_t size, const Allocator& alloc, const void* container) { void* allocated_memory = std::malloc(size); if (!allocated_memory) { throw std::bad_alloc(); } alloc.track(allocated_memory, container); return allocated_memory; } void operator delete(void* p, const Allocator& alloc, const void* container) { alloc.release(p, container); std::free(p); } int main() { Allocator alloc; int* i = new (alloc, NULL) int; operator delete(i, alloc, NULL); } 并使用函数operator new

deallocate

您应该考虑的一个案例是当#include <new> #include <cstdlib> struct Allocator { void track(void* p, const void* container) const; void release(void* p, const void* container) const; }; void* operator new (size_t size, const Allocator& alloc, const void* container) { void* allocated_memory = std::malloc(size); if (!allocated_memory) { throw std::bad_alloc(); } alloc.track(allocated_memory, container); return allocated_memory; } template<typename T> void deallocate(T* p, const Allocator& alloc, const void* container) { p->~T(); alloc.release(p, container); std::free(p); } int main() { Allocator alloc; int* i = new (alloc, NULL) int; deallocate(i, alloc, NULL); } 成功但构建对象失败时该怎么做。也就是说,当有足够的内存时,还有一些其他原因导致无法创建对象。该系统实际上足够智能,可以调用权限new(具有系统认为应具有的签名的delete)来释放分配的内存;但只有当delete有该签名时才有。您可能希望重载delete 提供operator delete函数,其中一个函数是根据另一个函数定义的:

deallocate

答案 1 :(得分:1)

这是我使用可变参数模板和宏的解决方案。

#include <cstdlib>
#include <new>
#include <iostream>
using namespace std;

#define MYNEW(Allocator, ...) (Allocator)->Alloc(__VA_ARGS__);

template<typename Type>
struct Allocator{
    template<typename... Args>
    Type* Alloc(Args... args){
        cout<<"weird allocator"<<endl;
        return new(malloc(sizeof(Type))) Type(args...);
    }
};

struct test{
    test(int a, bool b, const char* c){
        cout<<"test constructor with a="<<a<<", b="<<b<<", c="<<c<<endl;
    }
};

int main(){
    Allocator<test> myallocator;
    test* obj=MYNEW(&myallocator, 3, true, "hello");
}

就个人而言,我认为没有一个干净的解决方案(读取,不要求你在没有可变参数的情况下明确写出Allocator将在你分配对象的地方做什么)东西。

您还可以查看将其他参数(其他指向内存的指针)传递给新的运算符重载(yes it is possible)。