VC6 Tips / C++での独自のallocatorの作り方テクニック

C++での独自のnew deleteの作り方テクニックを見てあの面倒な規制をどうにかしたいと思っていた。
そんな時、STLはどのようにしてデストラクタ問題を解決していたのかと言う疑問が浮かんだ。
調べていくうちにアロケーターというキーワードに行き着いた。


std::vectorやstd::listのtemplateの第二引数を覚えているだろうか・・・
Aとか書いていたりAllocatorとか書いていたりする。

ここに自分で作ったallocatorを指定すると独自のポリシーを適用する事が出来る。
デフォルトではSTLで用意されたstd::allocatorが使われる。

なるほど。new deleteの機能をラップしたクラスとしてAllocatorを作ったと?
Allocatorの中身はC++の教科書には載っていないトリッキーな記述が目立ち非常に分かりに所や要点は随時コメントにて説明をするので我慢して見てほしい。
続きを読む

#include <vector>
#include <iostream>

template <class T > 
class my_allocator {
public:
  //typedef でクラス内や外部で使われるタイプを宣言
  //size_t等でエラーが出る場合はstd::size_tとしてみてください。
  typedef size_t    size_type;
  typedef ptrdiff_t difference_type;
  typedef T*        pointer;
  typedef const T*  const_pointer;
  typedef T&        reference;
  typedef const T&  const_reference;
  typedef T         value_type;

  template <class U>
  struct rebind { typedef my_allocator<U> other; };
  
  ///参照からポインター化  
  pointer       address(reference x) const
    { return &x; }
  const_pointer address(const_reference x) const
    { return &x; }
  
  ///メモリ確保  
  pointer       allocate(size_type n, const void* hint = 0) {
    //operator new()として呼び出した場合はmallocと
    //同じ扱い(byte単位確保)なので n * sizeof(T)
    return (pointer)operator new(n * sizeof(T));
    //return malloc(n * sizeof(T);
  }
  ///メモリ解放
  void deallocate(pointer p, size_type n = 0) {
    if(p==NULL){
      return;
    }
    operator delete(p);
    //free(p);
  }
  size_type max_size() const
  { 
    size_type c = (size_type)(-1) / sizeof(T);
    return (0<c ? c : 1); 
  }
    
  void construct(pointer p, const T& val) {
    /**
    何故この方法でコンストラクタのみが呼び出せるか
    分からないがイディオムとして私は覚えている。
    */
    new ((void*)p) T(val);
  }

  void destroy(pointer p) {
    /**
    デストラクターを呼び出す機能である
    templateとして入れたタイプ(型)がTなのでそれを用いて
    デストラクタをタイプに合わせて呼び出せるようにしている。
    */
    ((T*)p)->~T();
  }


  //STLPort用

  ///私の環境のSTLPort4.5.3ではこれが必要です。
  template <class _Tp1, class _Tp2>
    inline my_allocator<_Tp2>& __stl_alloc_rebind(
    my_allocator<_Tp1>& __a, const _Tp2*) 
  {
    return (my_allocator<_Tp2>&)(__a);
  }
  ///STLPortではこれも必要だと思います。
  template <class _Tp1, class _Tp2>
  inline my_allocator<_Tp2> __stl_alloc_create(
      const my_allocator<_Tp1>&, const _Tp2*
  ){ 
    return my_allocator<_Tp2>(); 
  }
};
using namespace std;
struct Test{
  Test(){
    cout << "construct" << endl;
  }
  ~Test(){
    cout << "destruct" << endl;
  }
};
int main(int argc,const char **argv)
{
  //dkcCheckMemoryLeak(TRUE);
  int i;
  my_allocator<Test> ma;
  Test *p = ma.allocate(2);//2個分確保
  for(i=0;i<2;i++){
    //&p[i]と p + i は同義
    ma.construct(&p[i],Test());//コンストラクタ
  }
  for(i=0;i<2;i++){
    ma.destroy(&p[i]);//デストラクタ
  }
  ma.deallocate(p);//解放
  
  
  //STLに使ってみる。
  std::vector<int,my_allocator<int> > vec;
  vec.push_back(1);
  vec.reserve(1024);
  vec.resize(2048);
  vec.push_back(2);

  return 0;
}



また、独自のアロケーター構築に関しては
http://www.scl.kyoto-u.ac.jp/scl/appli/appli_manual/SUNWspro/WS6U2/ja/manuals/stdlib/user_guide/general/15_3.htm
が詳しいので分からない事はこちらを見て欲しい。

これで独自のheap管理機能を作ったので自分専用のallocatorが作りたい!!!
といった要望にも答える事が出来る。
\(^_^)/