#include <iostream>
#include <memory>
#include <cassert>

struct Hoge {
  int i;
public:
  Hoge(int j) : i(j) {}
  void print() const {
    std::cout << i << std::endl;
  }
};

template <class T, class Allocator = std::allocator<T> >
class MyVector {
  int Elements;
  int Num;
  T* hp;
  Allocator alloc;
public:
  MyVector() : Elements(0), hp(0) {}
  void push_back(const T& h) {
    T* newmem = alloc.allocate(Elements + 1); // 要素を一つ増やすと再割当てが起きる可能性がある
    std::uninitialized_copy(hp, hp + Elements, newmem); // 古い要素を新しいメモリーにコピーする
    alloc.construct(newmem + Elements, h); // 追加された要素のコンストラクタ
    // 再割当てが起きたと見なして古い要素を破棄・メモリを解放する
    for (int i = 0; i < Elements; i++)
      alloc.destroy(&hp[i]);
    alloc.deallocate(hp, Elements);
    Elements++;
    Num++;
    hp = newmem;
  }
  T pop_back() {
//    std::assert(Elements != 0);
    T tmp = hp[--Elements];
    alloc.destroy(&hp[Elements]); // 要素を破棄(破棄してもメモリは解放されない事に注意、これがC++11でstd::shrink_to_fit()が用意された理由でもある)
    return tmp;
  }
  int size() const {
    return Elements;
  }
  T& operator[](int i) {
//    std::assert(Elements != 0);
    return hp[i];
  }
  ~MyVector() {
    for (int i = 0; i < Elements; i++)
      alloc.destroy(&hp[i]);
    alloc.deallocate(hp, Num);
  }
};

int main()
{
  MyVector<Hoge> mh;
  mh.push_back(Hoge(1));
  mh.push_back(Hoge(2));
  std::cout << "size = " << mh.size() << std::endl;
  mh[0].print();
  mh[1].print();
  mh.pop_back();
  std::cout << "size = " << mh.size() << std::endl;
}