STLのコンテナで自作のallocatorを使う

特に理由もなく、突然自作allocatorをSTLコンテナ、文字列で試したいです。

STLで、デフォルトのstd::allocatorがある。vectorを使う時、allocatorを指定しなかったらそれを使う。
VC2010のSTLのvectorヘーダに、こういうコードがある


template<class _Ty,
class _Ax = allocator<_Ty> >
class vector
そしてこんな構造体がある
explicit vector(const _Alloc& _Al)
じゃ、自分のallocatorを指定すると、自分でメモリ管理できる。

よくみると、xmemoryにstd::allocatorはこんなもんなんだ


#define _ALLOCATOR allocator
#define _PDFT ptrdiff_t
template<class _Ty>
class _ALLOCATOR
: public _Allocator_base<_Ty>
{ // generic allocator for objects of class _Ty
public:
typedef _Allocator_base<_Ty> _Mybase;
typedef typename _Mybase::value_type value_type;

typedef value_type _FARQ *pointer;
typedef value_type _FARQ& reference;
typedef const value_type _FARQ *const_pointer;
typedef const value_type _FARQ& const_reference;

typedef _SIZT size_type;
typedef _PDFT difference_type;

template<class _Other>
struct rebind
{ // convert this type to _ALLOCATOR<_Other>
typedef _ALLOCATOR<_Other> other;
};

pointer address(reference _Val) const
{ // return address of mutable _Val
return ((pointer) &(char&)_Val);
}

const_pointer address(const_reference _Val) const
{ // return address of nonmutable _Val
return ((const_pointer) &(char&)_Val);
}

_ALLOCATOR() _THROW0()
{ // construct default allocator (do nothing)
}

_ALLOCATOR(const _ALLOCATOR<_Ty>&) _THROW0()
{ // construct by copying (do nothing)
}

template<class _Other>
_ALLOCATOR(const _ALLOCATOR<_Other>&) _THROW0()
{ // construct from a related allocator (do nothing)
}

template<class _Other>
_ALLOCATOR<_Ty>& operator=(const _ALLOCATOR<_Other>&)
{ // assign from a related allocator (do nothing)
return (*this);
}

void deallocate(pointer _Ptr, size_type)
{ // deallocate object at _Ptr, ignore size
::operator delete(_Ptr);
}

pointer allocate(size_type _Count)
{ // allocate array of _Count elements
return (_Allocate(_Count, (pointer)0));
}

pointer allocate(size_type _Count, const void _FARQ *)
{ // allocate array of _Count elements, ignore hint
return (allocate(_Count));
}

void construct(pointer _Ptr, const _Ty& _Val)
{ // construct object at _Ptr with value _Val
_Construct(_Ptr, _Val);
}

void construct(pointer _Ptr, _Ty&& _Val)
{ // construct object at _Ptr with value _Val
::new ( (void _FARQ *)_Ptr) _Ty(_STD forward<_Ty>(_Val) );
}

template<class _Other>
void construct(pointer _Ptr, _Other&& _Val)
{ // construct object at _Ptr with value _Val
::new ( (void _FARQ *)_Ptr) _Ty(_STD forward<_Other>(_Val) );
}

void destroy(pointer _Ptr)
{ // destroy object at _Ptr
_Destroy(_Ptr);
}

_SIZT max_size() const _THROW0()
{ // estimate maximum array size
_SIZT _Count = (_SIZT)(-1) / sizeof (_Ty);
return (0 < _Count ? _Count : 1);
}
};

他のは理解しやすい。自作の時でも同じなんだ。
気になるところは:
1、allocate関数。メモリブロックはここから取り出すようだ。
2、deallocate関数。メモリブロックはここに返還する。
3、difference_typeってなに?
4、rebindの型。...なにこれ?

3のこと、ネットで「ptrdiff_t」を検索すると、このページが出る

http://www.cplusplus.com/reference/clibrary/cstddef/ptrdiff_t/

ポインタの-演算子の戻り値の型です。stddef.hにもあるんだ。放置しても大丈夫よね。

4のこと、ネットで検索するとこのページが出る

http://msdn.microsoft.com/en-us/library/5fk3e8ek(v=vs.80).aspx

なるほど、別の型用のallocatorだ。必要な時自身で作れるの。vectorの内部、T以外のオブジェクトも必要なんだ。例えば右の値の参照演算子とかなんとか

じゃ自作allocatorしたいなら、std::allocatorから継承して、allocate関数とdeallocate関数を書いて、そしてrebindの型を定義したらよさそうだ。

allocateとdeallocateは仮想関数じゃなくどうして動的な多態できるのだろう?
原因は簡単です:デフォルトのstd::stringは、std::basic_string, std::allocator >だから、今は std::basic_string, (自分のallocator) >なるので、allocatorの型は別にstd::allocatorじゃないから、仮想関数にする必要がない。

例え:

#include <memory>
#include <cstdlib>

#include <iostream>

template<class T>
class myallocator : public std::allocator<T> {
public:
myallocator() { }
myallocator(const myallocator& x) { }

template<class U>
myallocator(const myallocator<U>& x) { }

pointer allocate(size_type n, const_pointer hint = 0) {
std::cout << "allocate " << n * sizeof(T) << "bytes" << std::endl;
return (pointer) std::malloc(n * sizeof(T));
}

void deallocate(pointer ptr, size_type n) {
std::cout << "free pointer " << (void*) ptr << std::endl;
std::free(ptr);
}

template<class U>
struct rebind { typedef myallocator<U> other; };
};

#include <vector>
#include <string>

int main() {
std::basic_string<char, std::char_traits<char>, myallocator<char> > mys;
std::vector<int, myallocator<int> > v;
v.push_back(1);
std::cout << v.front() << std::endl;
v.erase(v.begin());

mys = "asdfasdfasdfasdfsadf";
std::cout << mys << std::endl;
}

実行すると、メモリの確保と解放の動作は見えるようになった。

mallocとfreeは自分の書いたメモリ管理コードに切り替えたらいろいろ試せる。