2

私が書いた古い循環バッファクラスをより堅牢にするために作り直しています。タイプ T のヒープにバッファーを割り当てています (そのため、クラスはテンプレート化されています)。ただし、動的に割り当てられたスペースへのポインターである T で可能なリソースの解放に問題があります。

これは、デフォルト値パラメーターを持つ ctor です。

template <typename T, unsigned int SIZE>
CircularBuffer(const T default_val) {
    _buffer = new T[SIZE];
    // assign each block default value, etc
}

// dtor
~CircularBuffer()
{
    delete [] _buffer;
}

ただし、たとえば誰かがこれを行うことにしたとします。

CircularBuffer<int*, 4> cb(new int); // buffer of 4, holding int*, with default value of new int

// later ~CircularBuffer call is made
// user allocated memory is not freed

このメモリを解放する (またはユーザーに解放させる) にはどうすればよいでしょうか? ユーザーの観点から手動で試しました:

delete cb.at(0);  // .at returns T& (so it would effectively return the pointer)
// above is access violation

デストラクタでこれを行う方法を見つけようとしましたが、コンパイラはテンプレート T がポインタではないと (たとえそうであったとしても) 考えているため、delete _buffer[i] を行うことができませんでした。

この状況を安全に処理できますか、または責任が私のものではないようにユーザーができることはありますか (クラスはこれを内部的に割り当てていないため、ユーザーはそうです)。

Edit* * * T* をテンプレート パラメーターとして渡すときに new を使用した割り当てでは、期待されるバッファー サイズが返されないことに気付きました。

// call default ctor
CircularBuffer<double*, 2> cb(); // _buffer = new T[SIZE];
// sizeof(_buffer) == 4;
// sizeof(double*) == 4;
// sizeof(double*[2]) == 8;  // _buffer should be this size, as it holds 2 4byte pointers, what happened?

これについて新しい質問を作成するか、元の質問をここに残すかはわかりませんが、これは以前に取得していたアクセス違反を説明していると思います (上記のインスタンスの _buffer[1] を逆参照しようとした後)。残念ながら私は何が原因なのかわからない。

4

2 に答える 2

5

1つのアイデアは、書くことによってテンプレートを部分的に特殊化することです

template<typename T, unsigned int SIZE>
class CircularBuffer<T*> { ... }

削除できるポインタを操作できるようにします。enable_if次に、ポインターに特化したテンプレートのコンパイルが失敗するように、いくつかの SFINAE トリックを使用できます。とにかく、最初に配列からのポインターが指すメモリを削除してから、配列自体を削除します。これは多くの可能性のある問題のように聞こえます。

他のアイデアは、ユーザーがメモリの一部を制御できるようにすることです。カスタム アロケータとデアロケータを定義するなどして、他のコンテナからアイデアをコピーできます。

template<typename T>
class Buffer {
public:
    typedef function<T(unsigned)> Allocator;
    typedef function<void(T)> Deallocator;

    Buffer(unsigned size,  Allocator allocator, Deallocator deallocator )
        : _size(size), _buffer(new T[1024]), 
          _allocator(allocator), _deallocator(deallocator)
    {
        for(unsigned i=0; i<_size; ++i)
            _buffer[i] = _allocator(i);
    };

    ~Buffer(){
        for(unsigned i=0; i<_size; ++i)
            _deallocator(_buffer[i]);

        delete[] _buffer;
    };

private:
    unsigned _size;
    Allocator _allocator;
    Deallocator _deallocator;
    T* _buffer;
};

...

Buffer<double*> b2(
    128, 
    [](unsigned idx) { return new double(idx + 0.123); }, 
    [](double* d) { cout << "deleting " << *d << endl; delete d; }
);

これは非常にシンプルで簡単なドラフトですが、アイデアを得る必要があります。


ところで、バッファーのサイズ ( unsigned int SIZE) にテンプレート パラメーターを使用することは、おそらく最良のアイデアではありません。コンパイラーは、サイズのみが異なるテンプレートに対して独立したコードを生成するため、実行可能サイズが大きくなります...SIZE構築引数を作成してみてください。一部のコンテナと同じように。

于 2013-07-20T05:44:56.190 に答える
5
template<typename T>
struct is_pointer{
    static const bool value = false;
};

template<typename T>
struct is_pointer<T*>{
    static const bool value = true;
};

template <bool, class T = void> 
struct enable_if 
{};

template <class T> 
struct enable_if<true, T> 
{ 
  typedef T type; 
};

template <bool, class T = void> 
struct disable_if 
{};

template <class T> 
struct disable_if<false, T> 
{ 
  typedef T type; 
};

template <typename T, unsigned int SIZE>
class CircularBuffer
{
public: 
    CircularBuffer(){
        _buffer = new T[SIZE];
    }

    ~CircularBuffer()
    {
        free_helper<T,SIZE>();
        delete [] _buffer;
    }

private:

    template<class U, unsigned int SIZE>
    void free_helper( typename enable_if< is_pointer<U>::value >::type *dummy=0 ){
        //pointer container
        for(int i=0;i<SIZE;++i){
           //delete it?  
           U t = _buffer[i];  
        }
    }

    template<class U, unsigned int SIZE>
    void free_helper( typename disable_if< is_pointer<U>::value >::type *dummy=0 ){
        //none pointer container
    }

    T* _buffer;
};


void main(){

    CircularBuffer<int,10> cb;
    CircularBuffer<int*,10> cb2;

}
于 2013-07-20T06:13:37.720 に答える