4

我的new宏有一个#define,以使用我自己的分配器,例如MYNEW(Type, Allocator)我将去哪里分配一些原始内存malloc,然后使用placement 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 来分配内存。但是,对于Type用于我的 Vector 的某些 ',没有默认构造函数,我无法判断构造函数可能需要多少变量。

有谁知道如何解决这个问题?(当然不用说使用 std:: 类型而不是我自己的类型。我这样做是为了了解更多信息)。我不希望只是超载operator new,因为我在那里有内存跟踪,因为我不希望内存跟踪两次(有更多的内部跟踪Alloc,但我展示的是简化的示例)并且宁愿只使用 malloc。

4

2 回答 2

4

看来您不仅在做自己的自定义分配器,而且您也不一定要尝试定义与 STL 兼容的分配器。但是,我认为认识到 STL 分配器的所有错误都是为了处理这种情况而设计的。 分配与构造分开:

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

它相当于[调用new复制现有对象的放置]。

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


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

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

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

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

#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函数,其中一个是根据另一个定义的:

void operator delete(void* p, const Allocator& alloc, const void* container)
{
    alloc.release(p, container);
    std::free(p);
}

template<typename T> void deallocate(T* p, const Allocator& alloc, const void* container)
{
    p->~T();
    operator delete(p, alloc, container);
}
于 2012-04-26T17:45:20.350 回答
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会做什么,在你分配对象的地方)。

您还可以查看将其他参数(指向内存的指针除外)传递给新的运算符重载(是的,这是可能的)。

于 2012-04-26T17:52:06.587 回答