4

私は自分のマクロに #define を持っており、new自分のアロケーターを使用して、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。私が試した 1 つのシナリオは、次のよう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 も使用していることです。ただし、TypeVector に使用される の一部には、デフォルトのコンストラクターがなく、コンストラクターに必要な変数の数がわからない場合があります。

この問題をどのように解決できるかについての洞察を持っている人はいますか? (もちろん、自分の代わりに std:: 型を使用することは言うまでもありません。詳細を知るためにこれを行っています)。メモリを2回追跡したくないので( への内部追跡がもっとありますが、私が示したのは単純化された例です)、単にmallocを使用しoperator newたいので、そこにメモリ追跡があるのでオーバーロードしたくありません。Alloc

4

2 に答える 2

4

独自のカスタム アロケータを実行しているだけでなく、必ずしも STL 互換のアロケータを定義しようとしているわけでもないようです。ただし、STL アロケーターはすべての障害に対して、このケースを処理するように設計されていることを認識しておくと便利だと思います。 割り当ては構築とは別です:

[ allocator::construct] は要素にスペースを割り当てないことに注意してください。すでに利用可能になっているはずですp(スペースを割り当てるメンバーallocateを参照してください)。

new[既存のオブジェクトをコピーする配置の呼び出し] に相当します。

一般的に言えば、2 つの選択肢があると思います。(1) オーバーロードoperator newしてメモリの割り当てを処理し、システムに構築を処理させるか、(2) アロケータ インターフェイスを STL アロケータ インターフェイスのようにします。


「オーバーロードoperator new」は、(1) 置き換える、または (2) の新しいバージョンを追加するという 2 つの意味でよく使用されnewます。operator new私が最初に答えたとき、私は最初の意味を使用していました(実際には「置換」または「オーバーライド」と呼ばれるべきoperator newです)。しかし、これについてもっと考えてみると、本当にオーバーロードoperator newすることが要件を満たすと判断しました。

オーバーロードを呼び出す構文operator deleteは非常に悪いため、多くの人がその理由だけでこの手法を避けていることに注意してください。をオーバーロードする代わりにdelete、関数 (たとえばdeallocate) を作成して使用できます。

このアプローチの価値は、メモリの割り当てをオブジェクトの構築から分離するという言語標準に従い、オブジェクトの構築はコンパイラに任せることです。転送演算子、デフォルト コンストラクターのないクラス、またはこれらの問題について心配する必要はありません。

オーバーロードoperator new/ operator delete(もちろん、私はあなたの肉付けに頼っていますtrack) 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);
}

考慮すべきケースの 1 つは、new成功したもののオブジェクトの作成に失敗した場合の対処方法です。つまり、十分なメモリがあるが、オブジェクトを作成できない他の理由がある場合です。deleteシステムは実際には、割り当てられたメモリを解放する権利(deleteシステムが持つべきだと考える署名を持つ)を呼び出すのに十分スマートです。deleteただし、その署名のある がある場合に限ります。関数をオーバーロードoperator delete して提供し、そのうちの 1 つをもう 1 つの観点から定義したい場合があります。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 に答える