これを実現するには、operator new[]とoperator delete[]を定義しなおす必要があります。以下に具体例を示します。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 |
void* operator new[](size_t size) throw(bad_alloc) { size_t t = size + sizeof(size_t); size_t* p = static_cast<size_t*>(::operator new(t)); *p = size; return p + 1; } void operator delete[](void* p) throw() { ::operator delete(static_cast<size_t*>(p) - 1); } |
実際にはこれ以外に、operator new[](size_t, void*)とoperator new[](size_t, const nothrow_t&)、およびこれらに対応するoperator delete[]も定義しなおす必要があります。また、境界調整にも配慮しなければなりません。
ただ、これだけだとnew式が返すポインタからsizeof(size_t)分引けばサイズを格納している部分のアドレスが取れるわけではありません。なぜなら、noocyteさんも書かれているように、処理系にもよりますが、デストラクタを呼び出す必要があるかどうかで、operator new[]が返す値がそのままnew式が返す値にならないことがあるからです。
これでは、結局もとの問題に逆戻りしてしまいます。そこで、operator new[]が返すポインタの直前にサイズを格納したのと同じように、線形リストを形成するためのポインタを格納するようにすれば、速度は遅くてもサイズを取得できるようになります。
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
struct new_node { size_t size; new_node* next; }; new_node* top = 0; void* operator new[](size_t size) throw(bad_alloc) { size_t t = size + sizeof(new_node); new_node* p = static_cast<new_node*>(::operator new(t)); p->size = size; p->next = top; top = p; return p + 1; } |
ところで、C++14からはサイズ付きのoperator delete[]をグローバルに定義できるようになったので、これを使えば目的の機能は実現できるかもしれません。けれども、もう解放する段階で要素数を取得できてもしかたがありませんね。どうしてもこういう機能が必要なら、素直にstd::vectorを使っておくのが一番だと思います。